finally绝大多数情况下会执行,但有三个例外:JVM退出、线程被强制中断、finally自身抛未捕获异常或死循环;推荐用try-with-resources替代手动close,它能自动逆序关闭资源并抑制异常。

finally 一定会执行吗?先说结论
绝大多数情况下会,但有三个例外:JVM 直接退出(System.exit())、线程被强制中断(Thread.stop(),已废弃但仍有影响)、或 finally 块里自己抛出未捕获异常/死循环。别默认“绝对可靠”,尤其在资源释放场景下,得看它到底有没有机会跑完。
用 try-with-resources 替代 finally 关流更安全
手动在 finally 里调用 close() 容易漏写、顺序错、或 close() 自己抛异常导致覆盖原异常。Java 7+ 的 try-with-resources 语句才是关闭 InputStream、OutputStream、Connection 等 AutoCloseable 资源的首选。
常见错误现象:finally 中 close() 抛 IOException,导致原始业务异常被吞掉;或者多个流嵌套时忘记关外层流。
-
try-with-resources会按声明逆序自动调用close(),且异常会被抑制(addSuppressed),不丢失主异常 - 必须确保资源类实现了
AutoCloseable接口(标准 IO 类都满足) - 不要在
try块里把流变量重新赋值为null,否则资源可能没被正确注册进自动关闭机制
try (FileInputStream fis = new FileInputStream("a.txt");
BufferedInputStream bis = new BufferedInputStream(fis)) {
// 读取逻辑
} // bis.close() 先调,fis.close() 后调,无需手动写 finally
finally 里不能 return 或 throw?不是不能,是危险
如果 try 或 catch 已经决定返回某个值,finally 里再写 return 会直接覆盖前者;同理,throw 会吞掉前面的异常。这不是语法错误,而是逻辑陷阱。
立即学习“Java免费学习笔记(深入)”;
使用场景:调试时想“无论如何都打个日志”,结果意外改了方法行为。
-
finally中只做副作用操作(如日志、状态重置、资源清理),绝不修改返回值或抛新异常 - 如果真需要统一返回处理,请用封装方法 + 显式变量控制,别依赖
finally的return - 注意基本类型返回值会被复制,但对象引用返回后,
finally里修改其字段仍可见——这点容易误判为“没覆盖”,实则状态已变
finally 和 JVM 优化有关吗?和字节码强相关
Java 编译器会把 try-finally 编译成重复的字节码块(jsr/ret 指令在旧版,新版用复制逻辑),所以 finally 块实际可能被执行多次(比如异常表匹配多条路径)。这不是 bug,是规范要求的“必定执行”语义落地方式。
性能影响:极小,但过度复杂逻辑放进去会影响 JIT 内联判断;兼容性上,Android Dalvik/ART 对 finally 的实现曾有差异(如早期 ART 不支持嵌套 finally 的某些边界情况)。
- 避免在
finally里做耗时操作(如网络请求、大对象构造) - 不要假设
finally只执行一次——尤其在有多个异常出口或重入场景下 - 用
javap -c看字节码,能直观确认finally是否被拆成多份插入
最常被忽略的一点:finally 不是事务边界,也不保证原子性。它只管“执行”,不管“成功”。关流失败、日志写崩、状态没回滚——这些都得你自己在 finally 里兜住,而不是指望它自动善后。










