在 Java 中,所有的异常的都继承了java.lang中的Throwable类,其主要有两个子类:

  • Exception:异常
  • Error:错误

Exception

Exception是程序本身可以处理的异常,可以通过catch语句捕获处理。主要分为两种:

  • Checked Exception:检查异常
  • Unchecked Exception:不检查异常

检查异常

Checked Exception 必须被try-catch或者throws处理,如果没有处理则无法通过编译。
除了RuntimeException及其子类以外,其他的Exception及其子类都属于受检查异常,例如IOExceptionSQLException

不检查异常

Unchecked Exception 又被称为运行时异常,即使不处理它也可以通过编译。RuntimeException及其子类都为不受检查异常,常见的有:

  • ArithmeticException:算术错误
  • NullPointerException:空指针错误
  • ClassCastException:类型转换错误
  • SecurityException:安全错误比如权限不够
  • ArrayIndexOutOfBoundsException:数组越界错误
  • IllegalArgumentException:参数错误比如方法入参类型错误
  • UnsupportedOperationException:不支持的操作错误比如重复创建同一用户
  • NumberFormatException:字符串转换为数字格式错误,是IllegalArgumentException的子类

例如NullPointerException,即使没有用try-catch语句捕获它或throws子句声明抛出它,也会编译通过。

异常处理

遇到异常时往往需要先抛出,然后捕获处理。

抛出

当一个方法出现错误引发异常时,方法创建异常对象并交付运行时系统,异常对象中包含了异常类型和异常出现时的程序状态等异常信息,运行时系统负责寻找处置异常的代码并执行,java 中使用throws抛出异常。

捕获

捕获及处理异常通常使用try-catch-finally。其中,try负责捕获异常,catch用于处理捕获的异常,而无论是否捕获或处理异常,finally的语句都会被执行。finally语句执行情况如下:

  • 当在trycatch中遇到return语句时,finally的语句将在方法返回之前被执行。
  • try语句和finally语句中都有return语句时,try语句中的return返回值会先被暂存在一个本地变量中,当执行到finally语句中的return之后,这个本地变量的值就变为了finally语句中的return返回值。
  • 两种情况下,finally中的语句不会执行:
    • 程序所在线程死亡。
    • 关闭 CPU。

注意事项

  • 抛出的异常信息一定要有意义。
  • 使用日志打印异常之后就不需要再抛出异常,两者不应该同时存在一段代码逻辑中。
  • 不要把异常定义为静态变量,因为这样会导致异常栈信息错乱。每次手动抛出异常,我们都需要手动new一个异常对象抛出。
  • 建议抛出更加具体的异常:比如字符串转换为数字格式错误的时候应该抛出NumberFormatException而不是其父类 IllegalArgumentException

Error

Error 属于程序无法处理的错误,例如OutOfMemoryErrorStackOverflowError等。这些错误发生时,JVM 会选择线程终止。错误是不可查的,所以不建议也不应该捕获它。

关于OutOfMemoryErrorStackOverflowError,可以参考:Java 内存泄漏和内存溢出

参考

  1. Java-Guide 异常
  2. 深入理解 java 异常处理机制