应精准捕获具体异常、在catch中执行有意义操作、用try-with-resources确保资源释放、包装异常时保留cause链。空捕获、笼统捕获、丢弃堆栈、忽略中断、手动close等均会掩盖问题。

避免在 catch 块中掩盖异常,关键不是“拦住错误”,而是让错误暴露得清楚、传递得完整、处理得恰当。空 catch、笼统捕获、丢弃堆栈、不恢复中断——这些做法看似让程序“不崩溃”,实则把问题锁进黑箱。
只捕获你能处理的具体异常
catch (Exception e) 或 catch (Throwable t) 会一并捕获 NullPointerException、OutOfMemoryError 甚至 JVM 错误,而这些本不该由业务层尝试恢复。真正该捕获的,是那些你明确知道如何应对的异常:
- 对文件操作,捕获 FileNotFoundException(提示用户检查路径)和 IOException(考虑重试或降级)
- 对数据库调用,单独捕获 SQLException,再根据 SQLState 或 ErrorCode 区分连接超时、唯一键冲突等场景
- 绝不把 RuntimeException 子类(如 IllegalArgumentException)和检查型异常混在一个 catch 里处理
catch 中必须做有意义的事
写一个 catch 块,就等于承诺“我能兜住这个错”。如果做不到,就不该捕获;如果能做到,就必须给出可验证的动作:
- 记录日志时,至少包含:业务标识(如 orderId)、异常类型、e.getMessage() 和完整的异常对象(即 logger.error("msg", e))
- 若选择降级(如查缓存替代远程调用),需确保降级逻辑本身不会抛新异常,且有监控埋点
- 遇到 InterruptedException,不能只吞掉,要调用 Thread.currentThread().interrupt() 恢复中断状态
用 try-with-resources 替代手动 close
资源未释放导致的异常掩盖很隐蔽:比如流关闭失败抛出 IOException,却覆盖了 try 块里原本更重要的 SQLException。Java 7+ 的 try-with-resources 能自动处理:
- 所有实现 AutoCloseable 的资源(FileInputStream、Connection、HttpClient)都可直接声明在 try 后括号内
- JVM 保证无论是否异常,close() 都会被调用;若多个资源关闭失败,后续异常会通过 addSuppressed() 关联,不丢失原始异常
- 避免在 finally 里手动 close 并再次 try-catch —— 容易因二次异常覆盖主异常
包装异常时务必保留 cause 链
把底层异常转成业务异常时,如果只写 new BusinessException("下单失败"),调用链就断了。排查时只能看到“失败”,看不到是网络超时还是库存扣减为空。
- 自定义异常构造函数中,始终传入原始异常作为 cause:super("下单失败", e)
- 使用 @ControllerAdvice 全局统一处理时,能沿异常链逐层提取根因,生成带 traceId 和 error code 的结构化响应
- 日志框架打印时,自然展开嵌套堆栈,开发人员一眼看到最开始抛出的位置
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











