finally块不一定执行。System.exit()或halt()会直接终止JVM,跳过finally;守护线程中finally可能因JVM退出而不运行;控制流阻塞(如死锁、无限循环)使程序无法到达finally;finally自身抛异常也会导致清理失败。

不一定。finally块在绝大多数常规流程中都会执行,但它不是铁律——只要程序控制流没机会走到那一步,它就不会运行。
System.exit() 或 halt() 会直接跳过 finally
这是最常见也最容易被忽视的失效点。一旦 try 或 catch 中调用 System.exit(0) 或 Runtime.getRuntime().halt(0),JVM 立即终止进程,字节码调度器根本不会把控制权交给 finally 块。
- System.exit() 会触发 shutdown hooks,但依然不执行 finally
- Runtime.halt() 更激进:不运行钩子、不调用 finalize,连 finally 都“没时间响应”
- 常见于测试强行退出、某些框架的紧急停机逻辑,需主动规避
守护线程里的 finally 可能压根不启动
守护线程(Daemon Thread)生命周期依附于非守护线程。只要 main 线程或其他用户线程结束,JVM 就立刻退出,不会等待守护线程执行完当前语句,更不会展开栈去执行其 finally。
- 哪怕 finally 是 try 块后的下一行代码,JVM 也不会执行
- 资源关闭、日志记录、状态更新等操作全部丢失
- 需要可靠清理的逻辑,必须放在非守护线程中,或改用 JVM shutdown hook(注意:hook 本身也不能写在守护线程里)
控制流卡住,finally 永远等不到
如果线程在 try 或 catch 中陷入无限循环、死锁,或调用未响应 interrupt 的阻塞方法(如 Object.wait() 未被唤醒、LockSupport.park() 未被 unpark),程序就无法推进到 finally 语句位置。
立即学习“Java免费学习笔记(深入)”;
- 这不是 finally “不执行”,而是控制流被阻塞,“执行不了”
- 避免在 try 块内做无超时机制的 IO、锁等待或长耗时计算
- 推荐用带超时的 API(如 Lock.tryLock(timeout)、Socket.setSoTimeout())提升可控性
finally 自身出错,执行了却等于没执行
finally 块运行了,但中途抛异常(比如 close() 时 IOException、NPE)、再次调用 System.exit(),或覆盖原始异常,会导致关键信息丢失或资源释放失败。
- 不要在 finally 中裸调 close(),应单独 try-catch 每个清理动作
- 若需保留原始异常,可用 addSuppressed() 把清理异常作为被抑制异常附加进去
- 优先使用 try-with-resources:资源自动关闭,语义更清晰,异常处理更健壮


















