多 catch 块必须按继承关系从子类到父类排列,同级异常可互换顺序但不可交叉捕获;自定义异常需语义明确、职责分离,受检异常用于强制处理场景,非受检异常用于逻辑错误,禁止滥用 Exception 兜底。

Java 中多 catch 块的编写不是自由组合,而是受编译器严格约束的结构化实践;异常接口(即 Exception 及其子类)的实现,核心在于“可识别、可区分、可处理”——不是为了抛出而抛出,而是为了让调用方能精准响应。
多 catch 块必须按继承关系从子到父排列
Java 编译器在编译期就强制校验 catch 块顺序。一旦某个异常类型能匹配抛出的实例(包括其子类),JVM 就立即执行该 catch 块,后续块被跳过。因此:
- 具体异常(如
ArithmeticException、ArrayIndexOutOfBoundsException)必须写在前面 - 泛化异常(如
RuntimeException、Exception)必须放在最后 - 若把
Exception写在NullPointerException前面,编译直接失败,报错:exception NullPointerException has already been caught
同级异常可任意排序,但不可重复或交叉捕获
没有继承关系的异常类型属于“同级”,例如 IOException 和 SQLException,它们互不兼容,顺序可互换,也不会触发编译错误:
- 允许:
catch (IOException | SQLException e)(Java 7+ multi-catch) - 禁止:
catch (Exception | RuntimeException e),因后者是前者的子类 - 禁止在同一 try 中先后写
catch (RuntimeException e)和catch (IllegalArgumentException e),后者是前者的子类
异常接口实现应聚焦语义明确与职责分离
自定义异常不是简单继承 Exception 或 RuntimeException,关键在于传达意图:
立即学习“Java免费学习笔记(深入)”;
- 受检异常(继承
Exception)适用于调用方必须决策的场景,比如业务规则校验失败(InsufficientBalanceException) - 非受检异常(继承
RuntimeException)适用于程序逻辑错误,如参数非法(InvalidOrderStatusException) - 所有自定义异常应提供有意义的构造函数(含 message、cause),避免空参默认构造
- 不建议让多个不同业务含义的异常共用同一父类接口,否则会削弱类型提示能力
避免用 Exception 兜底掩盖问题
末尾的 catch (Exception e) 看似稳妥,实则危险:
- 它会吞掉本该暴露的编程缺陷,比如
NullPointerException或ClassCastException - 日志中若只打印
e.getMessage(),将丢失堆栈,无法定位原始位置 - 真正需要兜底的场景极少,仅限于顶层入口(如
main方法)或线程未捕获处理器 - 若必须保留,至少调用
e.printStackTrace()或通过日志框架记录完整堆栈



















