被压抑异常是try-with-resources中close()抛出但未覆盖主异常的附加异常,调用主异常的getSuppressed()可获取其数组;多资源逆序关闭时后关闭者异常先被压抑,需在日志或测试中主动处理。

在 try-with-resources 中,如果 try 块抛出异常,同时资源关闭时也抛出异常,后者会被“压抑”(suppressed),并附加到主异常上;调用 getSuppressed() 即可获取这些被压抑的异常数组。
为什么会有被压抑的异常
try-with-resources 要求资源实现 AutoCloseable。JVM 会自动在 try 块结束(无论正常还是异常退出)后调用 close()。若 try 块已抛出异常 A,而 close() 又抛出异常 B,则 B 不会覆盖 A,而是作为“被压抑异常”附加到 A 上——这是为了不掩盖原始错误源。
如何用 getSuppressed() 拿到被压抑的异常
getSuppressed() 是 Throwable 类的方法,返回 Throwable[]。只有当异常对象确实有被压抑异常时,该数组才非空。
- 必须在捕获的异常对象上调用,通常是
catch (Exception e)中的e - 直接调用
e.getSuppressed(),返回一个可能为空的异常数组 - 遍历该数组,可打印、记录或做进一步处理
示例:
立即学习“Java免费学习笔记(深入)”;
try (FileInputStream fis = new FileInputStream("test.txt");
BufferedReader reader = new BufferedReader(new InputStreamReader(fis))) {
reader.readLine(); // 可能抛 IOException
} catch (IOException e) {
System.out.println("主异常: " + e.getMessage());
for (Throwable suppressed : e.getSuppressed()) {
System.out.println("被压抑异常: " + suppressed.getMessage());
}
}
注意点和常见误区
-
getSuppressed()返回的是“副本数组”,修改它不影响原异常的压制关系 - 只有主异常(即 try 块中抛出的那个)才可能有被压抑异常;被压抑异常自身不会继续压制其他异常
- 如果 try 块没抛异常,但 close 抛了,那这个 close 异常就是主异常,没有被压抑异常
- 多个资源按声明逆序关闭,若多个 close 都失败,后关闭的资源异常会先被压抑,先关闭的异常可能成为最外层被压抑项
调试和日志中的实用建议
生产环境建议在日志中显式输出被压抑异常,否则容易漏掉关闭阶段的关键问题(如连接未释放、文件句柄泄漏等)。
- 使用 SLF4J 或 Log4j 时,可手动拼接日志字符串包含
getSuppressed()内容 - JDK9+ 的
PrintWriter和printStackTrace()默认已递归打印被压抑异常,但自定义日志逻辑仍需主动处理 - 单元测试中可断言
e.getSuppressed().length > 0来验证资源关闭行为是否符合预期


















