finally块应仅执行无副作用的资源清理逻辑,如安全关闭流、重置标志位等,且须避免抛异常、return或线程同步操作。

finally 块里只放确定能执行且无副作用的清理逻辑
finally 块不是“兜底执行任意代码”的地方,而是为保障资源释放而设的最后防线。它会在 try 或 catch 执行完毕后**无条件执行**(除非 JVM 退出、System.exit()、线程被强制中断或发生致命错误如 OutOfMemoryError)。所以适合写的代码必须满足两个前提:一是**不会抛出新异常干扰原有异常传播**,二是**不依赖 try/catch 中可能已失效的对象状态**。
典型适用场景包括:
-
close()已打开的InputStream、OutputStream、Connection、Statement等资源(但要注意 JDK 7+ 更推荐用 try-with-resources) - 重置共享标志位,比如将
isProcessing = false(前提是该变量在 finally 外部定义且线程安全) - 释放本地句柄(如 JNI 调用后的
DeleteGlobalRef),但需确保句柄非 null 且未被提前释放
避免在 finally 中调用可能抛异常的方法
如果 finally 块里调用 close() 抛出 IOException,而 try 块中已有未捕获的异常(比如 NullPointerException),那么原始异常会被吞掉——JVM 只会抛出 finally 中的新异常,导致问题定位困难。
正确做法是捕获并处理或记录内部异常,而不是放任其冲垮调用栈:
立即学习“Java免费学习笔记(深入)”;
finally {
if (inputStream != null) {
try {
inputStream.close();
} catch (IOException e) {
// 记录日志,但不 throw
logger.warn("Failed to close inputStream", e);
}
}
}
注意:try-with-resources 会自动抑制(suppressed)关闭异常,保留主异常,比手写 finally 更安全。
不要在 finally 中 return、break 或 continue
这会覆盖 try/catch 中的返回值或控制流,造成逻辑错乱。例如:
public int getValue() {
try {
return 1;
} finally {
return 2; // 这个 return 生效,调用方永远拿不到 1
}
}
同理,在循环中使用 break 或 continue 会导致外层循环行为不可预测;在方法中 return 直接终结方法,跳过后续逻辑。这种写法违反直觉,也极难调试。
常见误用还包括在 finally 中修改返回变量(如 result = null),但若方法是按值返回(基本类型或不可变对象),该修改无效;若返回的是可变对象引用,则可能引发空指针或状态不一致。
finally 不是线程同步或状态校验的合适位置
有人试图在 finally 中加锁、校验业务状态、触发回调或发通知,这是危险的。因为 finally 的执行时机不可控:它可能在异常中途、资源部分初始化失败、甚至对象构造未完成时运行。此时锁对象可能为 null,校验依赖的字段可能还没赋值,回调函数可能收到半成品对象。
例如:
public void process() {
Lock lock = null;
try {
lock = new ReentrantLock();
lock.lock();
// ... 业务逻辑
} finally {
if (lock != null) lock.unlock(); // 看似合理,但 lock 构造失败时 lock 仍为 null
}
}
更稳妥的方式是把资源获取和释放绑定在同一作用域内,优先用 try-with-resources,或确保资源变量在 try 前初始化(如 Lock lock = new ReentrantLock()),再在 finally 中安全释放。
真正需要跨阶段协调的状态管理,应该由上层调用方或专门的生命周期管理器负责,而不是塞进 finally。









