目录
异常的类别
示例1:
示例2:
注意
自定义异常
总结自廖雪峰老师的Java教程: Java教程 - 廖雪峰的官方网站 (liaoxuefeng.com)https://www.liaoxuefeng.com/wiki/1252599548343744
异常的类别
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
}
}
从下往上看, 调用层级关系
main() 👉 process1() 👉 process2() 👉
👉 Integer.parseInt(String) 👉 Integer.parseInt(String, int)
查看Integer.java
源码可知,抛出异常的方法代码如下:
示例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();
}
}
由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可以根据父类快速生成子类的构造方法。