java异常捕获必须遵循“子类在前、父类在后”原则,否则编译报错“unreachable catch block”;具体顺序应为:业务异常→受检异常→运行时异常→exception兜底,确保精准处理与调试。

因为 Java 编译器按 catch 块从上到下的顺序匹配异常类型,一旦某个 catch 参数能接收当前抛出的异常(即该异常是其声明类型的实例或子类),就立即执行该块,后续 catch 不再检查。如果把 Exception 这种宽泛的父类写在最前面,它会捕获所有继承自它的子类异常——比如 IOException、SQLException、NullPointerException 等,导致后面更具体的 catch 块永远无法执行。
编译器直接拒绝“不可达代码”
Java 把这种后续永远执行不到的 catch 块判定为“不可达代码(Unreachable catch block)”,属于编译期语法错误,不是警告,而是强制报错。IDE 会立刻标红并提示类似:
- Unreachable catch block for IOException. This exception is already caught by the preceding catch block
不只是 Exception,所有父子异常都适用
这个规则不限于 Exception,任何存在继承关系的异常类型都必须遵守“子类在前、父类在后”:
- ✅ 正确:
catch (FileNotFoundException e)→catch (IOException e) - ❌ 错误:
catch (IOException e)→catch (FileNotFoundException e)(编译不通过) - ✅ 同级异常(如
IOException和SQLException)无继承关系,顺序可任意
实际影响远不止编译失败
即使绕过编译检查(极罕见),运行时也会丧失对具体异常的区分能力:
- 无法针对
SQLTimeoutException做重试,只能统一记日志 - 无法区分
IllegalArgumentException(参数错)和IllegalStateException(状态错),修复方向容易偏差 - 调试时看不到原始异常类型,排查成本显著升高
推荐的捕获顺序
应严格遵循“从具体到宽泛”的原则:
- 先捕获最具体的业务异常(如
InsufficientBalanceException) - 再捕获标准受检异常(如
SQLException、IOException) - 接着是常见运行时异常(如
IllegalArgumentException、NullPointerException) - 最后才考虑兜底的
catch (Exception e),且通常仅用于日志记录 + 重新抛出,不建议静默吞掉











