Java中catch块顺序须按“子类在前、父类在后”且优先体现业务处理优先级,如支付服务中PayBalanceInsufficientException→PayTimeoutException→RuntimeException;禁止将Exception放首位;检查型异常同理需按语义分层;慎用multi-catch掩盖业务差异。

Java中catch块的顺序不是随意安排的,必须遵循“子类异常在前、父类异常在后”的原则,否则编译不通过;但更重要的是,这个顺序要真实反映业务逻辑的处理优先级——哪些错误该精确捕获并特殊应对,哪些只需兜底记录或统一转化。
按业务语义分层:从具体到宽泛
不要只盯着继承关系排顺序,先想清楚业务场景中哪些异常代表可预期的、需差异化响应的问题。比如支付服务中:
- PayBalanceInsufficientException(余额不足)→ 引导用户充值,返回特定错误码和提示
- PayTimeoutException(第三方支付超时)→ 启动异步查单,前端提示“处理中,请稍候”
- RuntimeException(未分类运行时异常)→ 记录全量日志+告警,返回通用失败信息
这三个异常即使没有直接继承关系,也应按业务重要性由高到低排列,确保更精准的处理逻辑优先命中。
避免用Exception或Throwable做第一层catch
把catch(Exception e)放在最前面,等于提前拦截所有检查型与非检查型异常,后续更具体的catch形同虚设。典型反例:
立即学习“Java免费学习笔记(深入)”;
try { /* ... */ }
catch(Exception e) { /* 这里吞掉了所有异常 */ }
catch(NullPointerException e) { /* 永远执行不到 */ }正确做法是:只在末尾用catch(Exception e)作兜底,且务必记录原始堆栈(log.error("unexpected error", e)),不能只打印e.getMessage()。
检查型异常(Checked)的顺序同样关键
IOException、SQLException等虽是检查型异常,但业务含义差异很大。例如文件导出功能:
- 捕获FileNotFoundException → 提示“模板文件不存在,请联系运维”
- 捕获AccessDeniedException(JDK7+)→ 提示“权限不足,无法读取目录”
- 捕获IOException → 统一标记为“导出失败,请重试”,不暴露底层细节
注意:AccessDeniedException是IOException的子类,必须放在IOException之前,否则会被父类提前捕获。
慎用多重catch(multi-catch)掩盖业务差异
像catch(IOException | SQLException e)写法虽简洁,但丢失了异常类型的语义区分能力。如果这两个异常对应完全不同的用户提示或补偿动作,强行合并反而增加后续判断负担(如用instanceof再分支),不如分开写更清晰、更易维护。

















