目录

​异常的类别​

​异常的捕获与抛出​

​打印出方法的调用栈 ​

​示例1:​

​示例2:​

​注意​

​自定义异常​


总结自廖雪峰老师的Java教程: ​​Java教程 - 廖雪峰的官方网站 (liaoxuefeng.com)[Java 学习笔记] 异常处理_抛出异常https://www.liaoxuefeng.com/wiki/1252599548343744​


 

异常的类别

[Java 学习笔记] 异常处理_子类_02

Error表示严重的错误,程序对此一般无能为力,例如:

  • OutOfMemoryError:内存耗尽
  • NoClassDefFoundError:无法加载某个Class
  • StackOverflowError:栈溢出

Exception则是运行时的错误,它可以被捕获并处理, 分为两大类:

  • RuntimeException以及它的子类;
  • 非RuntimeException(包括IOException、ReflectiveOperationException等等)

不需要捕获的异常,包括​​Error​​​及其子类,​​RuntimeException​​及其子类,

编译器对RuntimeException及其子类不做强制捕获要求,不是指应用程序本身不应该捕获并处理RuntimeException。是否需要捕获,具体问题具体分析

异常的捕获与抛出

多个​​catch​​语句只有一个能被执行。例如:

public static void main(String[] args) {
try {
process1();
process2();
process3();
} catch (IOException e) {
System.out.println(e);
} catch (NumberFormatException e) {
System.out.println(e);
}
}

存在多个​​catch​​​的时候,​​catch​​的顺序非常重要:子类必须写在前面。例如:

public static void main(String[] args) {
try {
...
...
} catch (IOException e) {
System.out.println("IO error");
} catch (UnsupportedEncodingException e) { // 永远捕获不到
System.out.println("Bad encoding");
}
}

​UnsupportedEncodingException​​​异常捕获不到,因为它是​​IOException​​​的子类。当抛出​​UnsupportedEncodingException​​​异常时,会被​​catch (IOException e) { ... }​​捕获并执行。

正确的写法是把子类放到前面:

public static void main(String[] args) {
try {
...
...
} catch (UnsupportedEncodingException e) {
System.out.println("Bad encoding");
} catch (IOException e) {
System.out.println("IO error");
}
}

打印出方法的调用栈 

通过​​printStackTrace()​​可以打印出方法的调用栈 

示例1:

public class Main {
public static void main(String[] args) {
try {
process1();
} catch (Exception e) {
e.printStackTrace();
}
}

static void process1() {
process2();
}

static void process2() {
Integer.parseInt(null); // 会抛出NumberFormatException
}
}

 [Java 学习笔记] 异常处理_构造方法_03

从下往上看, 调用层级关系

main()  👉 process1()  👉  process2() 👉

👉  Integer.parseInt(String)  👉  Integer.parseInt(String, int)

查看​​Integer.java​​源码可知,抛出异常的方法代码如下:

[Java 学习笔记] 异常处理_子类_04

 [Java 学习笔记] 异常处理_1024程序员节_05

示例2:

public class demo {
public static void main(String[] args) {
try {
process1();
} catch (Exception e) {
e.printStackTrace();
}
}

static void process1() {
try {
process2();
} catch (NullPointerException e) {
throw new IllegalArgumentException(e);
}
}

static void process2() {
throw new NullPointerException();
}
}

[Java 学习笔记] 异常处理_java_06

 由Caused by: java.lang.NullPointerException可知,问题的根源在于​​Main.process2()​​方法抛出的​NullPointerException​

注意

  • 在代码中获取原始异常可以使用​​Throwable.getCause()​​方法。如果返回​​null​​,说明已经是“根异常”了
  • 捕获到异常并再次抛出时,一定要留住原始异常,否则很难定位第一案发现场
  • 在​​catch​​中抛出异常,不会影响​​finally​​的执行。JVM会先执行​​finally​​,然后抛出异常    (若是finally中抛出了异常, catch所捕捉到的异常便会丢失, 像是中途被调包了似的, 这叫做异常屏蔽)


自定义异常

(引用自​​自定义异常 - 廖雪峰的官方网站​​)

保持一个合理的异常继承体系

一个常见的做法是自定义一个​​BaseException​​作为“根异常”,然后,派生出各种业务类型的异常。

​BaseException​​​需要从一个适合的​​Exception​​​派生,通常建议从​​RuntimeException​​派生:

public class BaseException extends RuntimeException {
}

其他业务类型的异常就可以从​​BaseException​​派生:

public class UserNotFoundException extends BaseException {
}

public class LoginFailedException extends BaseException {
}

...

自定义的​​BaseException​​应该提供多个构造方法:

public class BaseException extends RuntimeException {
public BaseException() {
super();
}

public BaseException(String message, Throwable cause) {
super(message, cause);
}

public BaseException(String message) {
super(message);
}

public BaseException(Throwable cause) {
super(cause);
}
}

上述构造方法实际上都是原样照抄​RuntimeException​。这样,抛出异常的时候,就可以选择合适的构造方法。通过IDE可以根据父类快速生成子类的构造方法。