必须记录日志、重抛异常或明确兜底,禁用空catch和e.printStackTrace();需用带上下文的日志、保留cause链、合理降级;通过静态检查、CI拦截、代码评审和全局异常处理器落实规范。

避免在 catch 块里静默吞掉异常,核心是让异常“有迹可循、有责可追”——不记录、不传播、不兜底,就等于没处理。
所有 catch 必须明确做一件事
空块 catch (Exception e) { } 或仅 e.printStackTrace() 是最大风险点。它们不会报错,但会让问题彻底消失。
- 记录带上下文的日志:用
logger.error("读取用户配置失败,uid={}", uid, e),确保异常对象e是最后一个参数 - 重抛或转为业务异常:如
throw new OrderProcessException("库存校验失败", e),保留原始 cause 链 - 有明确兜底才捕获:比如网络超时后返回缓存值,并配
logger.warn("降级使用本地缓存,key={}", key) - 真要忽略,必须加注释说明原因和依据,例如:
// 忽略 InterruptedException,因当前线程非中断敏感任务
禁用高危写法,从源头卡住
某些模式几乎等同于主动埋雷,团队应统一禁止:
-
catch (Exception e) { }、catch (Throwable t) { } -
catch (IOException e) { e.printStackTrace(); }(控制台输出生产环境不可见) -
try-with-resources中的空catch,比如关闭流失败也被吞 -
@Transactional方法内catch后既不重抛也不标记回滚,导致事务意外提交
用工具和机制代替人工盯防
靠自觉容易漏,靠流程才能守住底线:
立即学习“Java免费学习笔记(深入)”;
- 在 SonarQube 或 SpotBugs 中启用规则
RSPEC-1或IllegalCatchCheck,自动拦截空 catch 和裸 Exception 捕获 - CI 流水线中加入静态检查,未通过则阻断合并
- 代码评审时把 “是否记录日志?是否保留 cause?能否向上抛?” 列为必查项
- 启动时注册全局钩子:
Thread.setDefaultUncaughtExceptionHandler((t, e) -> log.error("未捕获异常: {}", t.getName(), e)),作为最后一道防线
别让 finally 成为新陷阱
finally 不是保险丝,而是异常覆盖高发区:
- 绝对禁止在
finally中写return或throw,否则原始异常永远丢失 - 资源关闭可能失败(如
close()抛IOException),应捕获后用addSuppressed()关联,而非放任覆盖 - 优先用
try-with-resources,JVM 自动处理抑制异常,根因清晰可见


















