finally块的核心作用是保障清理逻辑的确定性执行,仅用于无副作用的资源清理,如安全关闭流、重置标志位、释放JNI句柄、记录日志;必须避免抛异常、return、break/continue,并用内层try-catch处理可能失败的操作;优先使用try-with-resources替代手写finally。

finally块的核心作用是保障清理逻辑的确定性执行,不是兜底运行任意代码的地方。它必须做到不干扰异常传播、不篡改返回值、不引入新风险。
只做无副作用的资源清理
适合放进finally的代码,必须满足“做了就做了,不做也不会破坏主流程”的特性:
- 安全关闭已打开的流、连接、通道等(前提是对象非null)
- 重置线程安全的共享标志位(如 isRunning = false)
- 释放JNI句柄或本地资源(需先判空、避免重复释放)
- 记录关键清理日志(仅记录,不依赖外部服务或网络)
所有操作都应假设try/catch中相关对象可能已处于异常状态,避免调用其方法引发NullPointerException等连锁问题。
绝不抛异常、不写return
finally中一旦抛出未捕获异常,会覆盖原始异常;一旦出现return,会直接终结方法并掩盖真实返回意图:
立即学习“Java免费学习笔记(深入)”;
- 如果try里发生NullPointerException,catch只记日志没重抛,而finally里close()又抛出IOException,调用方只能看到后者——原始bug彻底丢失
- 哪怕try里写return 42,finally里一句return -1,结果永远是-1,且异常也会被吞掉
- break/continue在循环中嵌套使用时,会让外层控制流不可预测,同样应禁止
清理失败要内部消化
对可能出错的操作(比如stream.close()),必须用内层try-catch包裹,异常只能记录或忽略,不能向外传播:
- 正确写法: if (stream != null) { try { stream.close(); } catch (IOException e) { logger.debug("close failed", e); } }
- 错误写法: stream.close(); // 任由IOException向上冒泡
- 这种防护不是为了掩盖问题,而是防止清理动作反成故障源
优先用try-with-resources替代手写finally
Java 7起,绝大多数资源管理场景都不该手动写finally:
- 语法简洁:try (FileInputStream fis = new FileInputStream("a.txt")) { ... }
- 异常不丢失:即使业务代码和close()都抛异常,主异常仍是业务异常,close异常会作为suppressed exception附加其中
- 自动判空与抑制:无需手动检查null,也不用担心重复关闭
- 适用范围包括AutoCloseable接口的所有实现,如InputStream、Connection、Scanner等
手写finally只应在无法使用try-with-resources的老环境(如Java 6)或极特殊资源管理场景下作为备选方案。


















