finally 总能执行是因为 JVM 在方法返回前强制插入其字节码;它在 return 记值后、异常抛出前、未捕获异常时均执行,但 System.exit、JVM 崩溃等极端情况除外。

finally 为什么总能执行,哪怕 try 里 return 了?
因为 JVM 在方法实际返回前,会强制插入 finally 块的字节码——它不是“逻辑上最后执行”,而是被编译器重写进控制流的硬性保障。这意味着:
- try 中写 return "ok",JVM 会先记下这个返回值,再跳进 finally 执行;
- catch 抛出新异常,finally 仍会在异常向上抛出前运行;
- 即使 try 里发生 NullPointerException 且没被 catch 捕获,finally 也照常执行(除非 JVM 已退出)。
资源关闭别只靠 finally,优先用 try-with-resources
手写 finally 关流容易漏判空、重复关、或忘记嵌套资源顺序。Java 7+ 推荐用 try-with-resources 自动管理:
try (FileInputStream fis = new FileInputStream("a.txt");
BufferedReader reader = new BufferedReader(new InputStreamReader(fis))) {
// 使用资源
} catch (IOException e) {
// 异常处理
}
// fis 和 reader 的 close() 会自动按声明逆序调用,无需 finally- 所有实现了
AutoCloseable的类(如Connection、Socket、Scanner)都支持 - 如果必须手写
finally(比如老 JDK 或自定义资源),记得加判空:if (conn != null) conn.close(); -
try-with-resources在异常发生时也会保证close()调用,且能抑制多个异常(通过addSuppressed)
finally 里写 return 是个危险操作
它会直接覆盖 try 或 catch 中的返回值,而且不报错、难调试:
public static String getValue() {
try { return "try"; }
finally { return "finally"; } // ⚠️ 调用结果永远是 "finally"
}- 基本类型和不可变对象(如
String)会被完全替换 - 可变对象(如
StringBuilder)若在finally中修改内容,会影响返回结果,但这是引用共享导致的副作用,不是“覆盖” - 这种写法会让返回逻辑脱离主干路径,建议彻底避免
finally 真的“一定”执行吗?这几种情况它会跳过
绝大多数场景下它可靠,但以下例外必须知道:
-
System.exit(0)在try或catch中被调用 → JVM 立即终止,finally不执行 - 线程被强制中断(如已废弃的
Thread.stop(),或Runtime.halt()) - JVM 崩溃(如
OutOfMemoryError触发致命错误,部分情况下finally可能来不及跑完) - 守护线程中发生未捕获异常且主线程已退出,整个 JVM 可能直接关闭
这些属于极端边界,日常开发中不必过度担忧,但写关键清理逻辑(比如释放锁、回滚事务)时,得结合上层机制(如 ReentrantLock.unlock() 的 try-finally 模式)来兜底。
立即学习“Java免费学习笔记(深入)”;









