Java中异常吞没本质是处理意图缺失导致的逻辑盲区,须禁止空catch和仅printStackTrace;所有catch必须记录完整堆栈、重抛或明确兜底;只捕获具体异常类型;包装时保留cause链;优先用try-with-resources并检查suppressed异常。

Java 中异常吞没,本质是让本该暴露的问题“静默消失”——代码继续运行,日志没有线索,调用方毫无感知。这不是语法错误,而是处理意图缺失导致的逻辑盲区。避免的关键不是少写 try-catch,而是每一步都明确“我为什么要捕获?捕获后做什么?”
禁止空 catch 和仅 printStackTrace()
空 catch 块(catch (Exception e) { })或只写 e.printStackTrace() 是吞没的头号原因。前者不记录、不响应、不传播;后者输出到 System.err,在容器、日志平台中基本不可见,也无时间、线程、业务 ID 等上下文。
- 所有 catch 块必须至少做一件事:记录完整堆栈、包装重抛、或明确兜底(需加注释说明理由)
- 统一使用 SLF4J 或 Log4j2,且异常对象必须作为最后一个参数传入:logger.error("读取订单失败 | orderId={}", orderId, e)
- 绝不用 log.error("失败: " + e.getMessage()) 或 log.info("出错了") —— 前者丢堆栈,后者错级别、无上下文
只捕获你能真正响应的具体异常类型
catch (Exception e) 或 catch (Throwable t) 会把 NullPointerException、OutOfMemoryError、StackOverflowError 全部吃掉,掩盖编码缺陷或系统级崩溃。中间层业务代码不应承担“防崩”责任。
你接手一个需求时,是不是经常这样:"改一个小字段 → 结果把不相关的代码也改了"、"需求说得很模糊 → 做着做着发现跑偏了"、"数据库加个字段 → 上线才发现漏了数据兼容"、"做了个新功能 → 一个月后自己都忘了当时怎么设计的"、"写到一半被中断 → 再回来不知道从哪继续";这个 Skill 就是专门解决这些问题的。 它会自动探测你的项目环境(JDK、Spring Boot、ORM、数据库、前端框架),记在
- 按实际可能抛出的异常分块捕获:比如 HTTP 调用用 catch (IOException | TimeoutException e),文件操作用 catch (FileNotFoundException | SecurityException e)
- DAO 层不捕获 Exception,而是向上声明 throws SQLException;Service 层不处理空指针,让它暴露为测试失败或监控告警
- 兜底逻辑统一收口到 Web 层(如 @ControllerAdvice)或网关层,而非在每个工具方法里重复写泛化 catch
包装异常时必须保留原始 cause 链
用 new RuntimeException("DB 操作失败") 替换原异常却不传入 e 作 cause,会导致调用链断裂。日志里只看到最后一行 throw,找不到真正出错位置和底层根因(比如是连接超时还是 SQL 语法错)。
立即学习“Java免费学习笔记(深入)”;
- 所有自定义异常包装都必须使用带 cause 的构造函数:throw new ServiceException("订单创建失败", e)
- 日志中也要用 logger.error("xxx", e),而不是 logger.error("xxx" + e) —— 后者只打 toString(),不输出堆栈
- 检查所用框架(如 Spring JDBC)是否自动保留 cause;不要默认它做了,要验证日志中能否看到嵌套的 Caused by
资源清理优先用 try-with-resources,finally 中不抛新异常
try 块已抛异常,finally 再抛一个,JVM 会丢弃原始异常,只留下 finally 的那个。排查时看到的全是“关闭失败”,完全被误导。
- 所有实现 AutoCloseable 的资源(FileInputStream、Connection、HttpClient)一律用 try-with-resources,无需手写 finally
- 若必须手写 finally(如老版本 JDK),对 close() 等可能失败的操作,应内部捕获并记录,但绝不向上抛:try { resource.close(); } catch (IOException ignored) { logger.warn("资源关闭异常,忽略", ignored); }
- 注意 try-with-resources 的压制异常(suppressed exceptions):在 catch 块中主动检查 e.getSuppressed() 并记录,避免关键关闭错误被隐藏

















