父类异常必须放在最后,因为异常捕获按代码顺序匹配,若宽泛类型(如exception)写在前面,会拦截所有子类异常导致后续catch块不可达,编译器直接报错unreachable catch block。

父类异常必须放在最后,是因为异常捕获是按代码顺序逐个匹配的——一旦前面的 catch 块能接住当前抛出的异常,后面的块就彻底失效。如果把 Exception 这类宽泛类型写在前面,它会“吃掉”所有子类异常(比如 IOException、SQLException、NullPointerException),导致后续更具体的处理逻辑完全无法触发。
编译器直接拒绝“不可达代码”
这不是警告,而是硬性编译错误。Java 编译器会检测到后面那些本该处理具体异常的 catch 块永远执行不到,直接报错:Unreachable catch block。例如:
- 错误写法:先 catch (Exception e),再 catch (IOException e) → 编译失败
- 正确写法:先 catch (FileNotFoundException e),再 catch (IOException e),最后 catch (Exception e)
顺序错了,不只是报错,更是逻辑丢失
即使绕过编译(现实中几乎不可能),运行时也会失去精准处理能力:
- SQLTimeoutException 和 SQLException 都被统一记日志,没法单独做重试
- IllegalArgumentException 和 IllegalStateException 混为一谈,修复方向容易跑偏
- 调试时只能看到 Exception,原始异常类型被掩盖,排查成本翻倍
哪些异常有顺序要求?哪些没有?
关键看继承关系:
- 必须守序:有父子关系的类型,如 FileNotFoundException ← IOException ← Exception,必须子类在前、父类在后
- 顺序自由:无继承关系的类型,如 IOException 和 SQLException,谁先谁后都合法,但建议按业务常见度排列
- 多异常捕获(|)也受限:catch (IOException | SQLException e) 合法;但 catch (IOException | FileNotFoundException e) 会编译报错,因后者是前者子类
推荐的实际排列顺序
按“从具体到宽泛”组织,兼顾可读性与可维护性:
- 最上面:自定义业务异常(如 InsufficientBalanceException)
- 中间层:标准受检异常(SQLException、IOException)
- 再往下:常见运行时异常(IllegalArgumentException、NullPointerException)
- 最底部:兜底的 catch (Exception e),仅用于统一日志 + 重新抛出,不建议静默吞掉











