异常屏蔽指捕获异常后不处理、不记录、不抛出,或丢弃原始异常cause导致根源丢失;应通过带cause构造异常、addSuppressed、完整日志、避免空catch来避免。

Java 中处理嵌套异常时的异常屏蔽与恢复,核心在于避免“丢失原始异常”和“掩盖真正问题”,同时确保关键错误信息不被覆盖、资源能正确释放、业务逻辑可合理降级或重试。
什么是异常屏蔽?
异常屏蔽(Exception Swallowing)指在 catch 块中捕获异常后,既不抛出、也不记录、甚至不做任何处理——比如只写个空的 catch (Exception e) {}。更隐蔽的情况是:外层 try-catch 捕获了内层抛出的新异常,却忽略了原始异常(即被“包装”掉的 cause),导致堆栈根源丢失。
例如:
try {
parseJson(input); // 可能抛出 JsonParseException
} catch (Exception e) {
throw new RuntimeException("解析失败"); // 原始异常被丢弃!
}
这样调用方看到的只有新异常,无法追溯到 JSON 格式错误的具体位置。
立即学习“Java免费学习笔记(深入)”;
Java项目代码review工具。分析Git变更+完整调用链路上下文,推断业务需求,进行多维度评分和分类汇总,生成完整PRD文档。包含细粒度Java代码审查清单(Null安全、异常处理、Streams、并发、equals/hashCode、资源管理、API设计、性能、MyBatis/ORM、事务边界、SQL/DD...
如何避免异常屏蔽?
关键是保留原始异常上下文,尤其是使用带 cause 构造器的异常类:
- 抛出新异常时,始终传入原始异常作为 cause:
throw new ServiceException("业务处理失败", e); - 使用
Throwable.addSuppressed()(Java 7+)记录被压制的异常,常见于 try-with-resources 中多个 close() 抛异常时; - 日志中不要只打
e.getMessage(),而要用logger.error("操作失败", e),确保堆栈完整输出; - 避免无意义的空 catch,哪怕只是打日志也比静默吞掉强。
嵌套场景下的恢复策略
不是所有异常都需要“恢复”,但对可预期的失败(如网络超时、临时文件不可读),应设计分层恢复机制:
-
内层专注具体错误类型:比如解析数字失败时捕获
NumberFormatException,转为默认值或跳过当前项; - 外层处理通用失败路径:如 IO 异常发生时,尝试从备份源加载、启用缓存数据、或返回兜底响应;
- 利用 finally 或 try-with-resources 确保清理:即使异常发生,连接、流、锁等资源仍能释放;
- 必要时主动重试:对外部依赖(如 HTTP 调用)的瞬时异常,可在外层加简单重试逻辑,而非层层向上抛。
检查异常 vs 运行时异常的协作
受检异常(如 IOException)强制你面对它,适合表示外部不确定性;非受检异常(如 IllegalArgumentException)反映代码缺陷,应尽早修复而非掩盖。
嵌套处理中建议:
- 内层遇到受检异常,若无法当场解决,用运行时异常包装后向上抛(如
new IllegalStateException("配置加载失败", e)),避免污染接口声明; - 外层统一捕获顶层运行时异常做兜底日志和响应封装,但不要用一个
catch (Exception e)拦住所有——要区分系统级错误(如 OOM)、业务异常(如余额不足)、技术异常(如 DB 连接中断); - 对明确可恢复的异常(如
SQLException中的连接超时),单独 catch 并触发恢复动作,而不是混进通用异常分支。

















