finally 在 try-with-resources 中不必要,因为 JVM 会自动按逆序调用资源的 close() 方法,无论 try 块是否抛出异常;手动添加 finally 关闭资源易导致重复关闭或异常掩盖。

在 try-with-resources 语句中,finally 块通常可以完全省略,因为资源的自动关闭已经由 JVM 保证。你不需要、也不应该再手动写 finally 来调用 close() —— 那样反而可能导致重复关闭或异常掩盖。
为什么 finally 在 try-with-resources 中不必要
try-with-resources 的核心设计目标就是替代传统 try-catch-finally 中“在 finally 里关流”的冗余模式。JVM 会在 try 块退出(无论正常结束还是抛出异常)后,自动按声明逆序调用每个资源的 close() 方法,且这个过程独立于你的 catch 或 finally。
- 即使 try 块中抛出异常,close() 仍会被调用
- 如果 close() 本身也抛出异常,它会被抑制(suppressed),主异常仍会抛出
- 手动在 finally 里再 close() 同一个资源,可能触发 IllegalStateException(如 Stream 已关闭)或 NullPointerException
什么情况下看似需要 finally,其实有更优解
常见误解是:想在资源关闭后做点“收尾工作”(比如清空缓冲、记录日志、重置状态),于是想塞进 finally。但更好的做法是:
- 把收尾逻辑放在 try 块末尾:只要它不依赖资源是否还打开,就直接写在 try 最后一行
- 用自定义 AutoCloseable 封装行为:在 close() 方法里同时做关闭 + 清理,这样 try-with-resources 自动触发整套逻辑
- 捕获并处理 close() 异常(如需):可通过 getSuppressed() 检查被抑制的异常,而不是靠 finally 拦截
对比:传统写法 vs try-with-resources(无 finally)
❌ 传统方式(冗余且易错):
try {
fis = new FileInputStream("a.txt");
// ... 读取
} catch (IOException e) {
throw e;
} finally {
if (fis != null) fis.close(); // 容易漏判空、重复关、掩盖主异常
}
✅ try-with-resources(简洁安全):
try (FileInputStream fis = new FileInputStream("a.txt")) {// ... 读取
} catch (IOException e) {
throw e;
}
// 没有 finally —— 关闭已自动完成
唯一需要 finally 的例外情况
极少数场景下,你确实需要 finally,但它和资源关闭无关,例如:
- 释放非 AutoCloseable 的系统资源(如 native 句柄,且无法包装成 AutoCloseable)
- 强制恢复线程上下文(如 ThreadLocal.remove()),且必须在任何退出路径执行
- 但注意:这些逻辑要和资源关闭正交,不能干扰 try-with-resources 的自动关闭流程
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











