# 简介

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

  • Exception:异常
  • Error:错误

    # Exception

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

  • Checked Exception:检查异常

  • Unchecked Exception:不检查异常

    | 检查异常

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

    | 不检查异常

    不检查异常又被称为运行时异常

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

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

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

# Error

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

# 异常处理

对于异常的处理往往有两种:

  • 抛出:throws
  • 捕获:try-catch-finally

    | 抛出异常

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

    | 捕获异常

    捕获异常使用 try-catch-finally 块处理,try 负责捕获异常,catch 用于处理捕获的异常,无论是否捕获或处理异常,finally 的语句都会被执行。

    * finally

  • 当在 try 或 catch 中遇到 return 语句时,finally 的语句将在方法返回之前被执行。

  • 当 try 语句和 finally 语句中都有 return 语句时,try 语句中的 return 返回值会先被暂存在一个本地变量中,当执行到 finally 语句中的 return 之后,这个本地变量的值就变为了 finally 语句中的 return 返回值。
  • 两种情况下,finally 中的语句不会执行:

    • 程序所在线程死亡。
    • 关闭 CPU。

      # 注意事项

  • 抛出的异常信息一定要有意义。

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

    # 参考

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