catch块必须遵循“子类在前、父类在后”顺序,否则编译报错;exception若写在前面会导致其子类catch块不可达;runtimeexception及其子类同理;推荐按具体到宽泛顺序捕获。
因为 java 编译器会按 catch 块从上到下的顺序匹配异常类型,一旦某个 catch 参数能接收抛出的异常(即该异常是其声明类型的实例或子类),就立即执行该块,后续 catch 不再检查。
Exception 写在前面会直接“吃掉”所有子类异常
比如 Exception 是 IOException、SQLException、NullPointerException 等绝大多数异常的父类。如果把它写在最上面:
- 哪怕实际抛出的是
FileNotFoundException,也会被catch(Exception e)匹配成功 - 后面专门处理
FileNotFoundException或IOException的catch块就永远无法到达 - 编译器判定这是“不可达代码(Unreachable catch block)”,直接报错,不通过编译
这不是运行时逻辑问题,而是编译期强制约束
Java 把这种顺序错误当作语法级问题拦截,不是靠测试或运行才发现。IDE(如 IntelliJ、Eclipse)会在你写完就标红提示,例如:
同理也适用于 RuntimeException 及其子类
虽然 RuntimeException 及其子类(如 NullPointerException、ArrayIndexOutOfBoundsException)属于非受检异常,不强制 try-catch,但一旦你显式捕获它们,同样要遵守“子类在前、父类在后”的顺序规则:
- 写成
catch(RuntimeException e)在前、catch(NullPointerException e)在后 → 编译失败 - 正确写法必须是
catch(NullPointerException e)在前,catch(RuntimeException e)在后(如有必要)
真正安全的写法是“从具体到宽泛”
推荐结构如下:
- 先捕获最具体的业务异常(如自定义的
InsufficientBalanceException) - 再捕获标准受检异常(如
SQLException、IOException) - 接着是常见运行时异常(如
IllegalArgumentException) - 最后才考虑兜底的
Exception(通常应避免,仅用于日志记录+重新抛出)











