规范异常处理核心是“可识别、可定位、可恢复”:须按具体子类从宽到窄分层捕获,禁用空catch和泛捕exception;资源操作强制使用try-with-resources;底层异常应封装为带业务上下文的自定义异常,全局处理器仅作兜底。

规范编写异常处理策略,核心是让异常“可识别、可定位、可恢复”,而不是用 catch (Exception e) 一揽子吞掉所有问题。这种写法看似省事,实则掩盖了异常类型、丢失上下文、阻碍排查,属于高危反模式。
只捕获你真正能处理的异常类型
Java 异常有明确继承体系,应按具体子类逐层捕获,而非用父类兜底。例如文件操作中:
- 先捕获
FileNotFoundException—— 表示路径错误或配置缺失,可提示用户检查配置 - 再捕获
SecurityException—— 表示权限不足,需引导运维调整策略 - 最后才考虑
IOException—— 覆盖其他不可预知的IO故障,统一做重试或降级
顺序必须由具体到宽泛,否则 IOException 会提前捕获并“吃掉”其子类,导致精细化处理失效。
拒绝空 catch 块,至少记录原始堆栈
哪怕暂时不处理,也不能静默吞异常。空 catch 是调试噩梦的起点:
Java项目代码review工具。分析Git变更+完整调用链路上下文,推断业务需求,进行多维度评分和分类汇总,生成完整PRD文档。包含细粒度Java代码审查清单(Null安全、异常处理、Streams、并发、equals/hashCode、资源管理、API设计、性能、MyBatis/ORM、事务边界、SQL/DD...
- ❌ 错误写法:
catch (Exception e) { } - ✅ 正确做法:
catch (SQLException e) { logger.error("订单入库失败(id={})", orderId, e); }
日志必须包含关键业务上下文(如订单号、用户ID)和完整异常堆栈,否则等于没记。
用 try-with-resources 替代手动 finally 关闭
资源泄漏常因异常打断关闭逻辑而发生。JDK 7+ 的 try-with-resources 可自动释放实现 AutoCloseable 的资源,且能正确抑制多重异常:
- ✅ 推荐:
try (Connection conn = dataSource.getConnection(); PreparedStatement ps = conn.prepareStatement(sql)) { ... } - ❌ 避免:
finally { if (conn != null) conn.close(); }—— close() 本身可能抛异常,掩盖主异常
向上抛出时做有意义的封装
底层异常(如 SQLException)对上层业务无意义,直接暴露会泄露技术细节。应在合适层级转换为语义清晰的自定义异常:
- 保留原始异常作为 cause:
throw new UserOperationException("修改用户邮箱失败", e); - 补充业务上下文:
new UserOperationException("用户" + userId + "邮箱格式非法", e); - 避免裸 throw new RuntimeException(e),它丢弃了所有业务含义
全局异常处理器作为兜底,不是替代方案
Spring Boot 中的 @ControllerAdvice 很有用,但它只是最后一道防线:
- 它适用于统一返回格式、记录未捕获异常、避免前端看到 500 页面
- 但不能代替业务层对
IllegalArgumentException做参数校验、对OptimisticLockException做重试等主动处理 - 全局处理器捕获到
Exception,恰恰说明某处本该精准捕获却漏掉了
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南










