Java 中的 finally 块:神话、现实与陷阱
\n\n每位 Java 开发者,从新手到资深专家,迟早都会遇到 finally 块。教科书上写着:“无论是否发生异常,该块都会执行。” 听起来像是一个铁定的保证,对吧?但让我们深入幕后看看。
在 StackOverflow 上,问题 “Does a finally block always get executed in Java?” 获得了数千次浏览和数百条评论。经验丰富的程序员争论不休,新手感到困惑,而实际案例表明:“总是”并非绝对真理。在本文中,我们将分析所有场景,编写真实代码,并查明 finally 何时可能让你失望。
finally 如何工作?基本示例
\n\n首先,经典用法。finally 块用于释放资源:关闭文件、数据库连接、套接字。它在 try(以及发生异常时的 catch)之后执行,但在控制权离开方法之前。
public class FinallyDemo {\n public static void main(String[] args) {\n try {\n System.out.println("在 try 内部");\n int result = 10 / 2; // 一切正常\n } catch (ArithmeticException e) {\n System.out.println("捕获到异常:" + e.getMessage());\n } finally {\n System.out.println("finally 块已执行!");\n }\n System.out.println("程序继续...");\n }\n}\n\n输出:
\n在 try 内部\nfinally 块已执行!\n程序继续...\n\n一切都在预料之中。但如果 try 中发生了一个未被捕获的异常,会发生什么?
public class FinallyWithUncaught {\n public static void main(String[] args) {\n try {\n System.out.println("尝试除以零...");\n int result = 10 / 0; // 异常!\n } finally {\n System.out.println("Finally 在异常向上传播之前执行");\n }\n // 这一行将不会执行\n System.out.println("这一行不会被打印");\n }\n}\n\n输出会让新手惊讶:
\n尝试除以零...\nFinally 在异常向上传播之前执行\nException in thread "main" java.lang.ArithmeticException: / by zero\n at FinallyWithUncaught.main(FinallyWithUncaught.java:5)\n\nfinally 块执行了,尽管异常未被处理。正是这种行为催生了“总是”的神话。但让我们深入挖掘。
finally 何时不执行?真实场景
\n\n至少有三种情况下 finally 可能不会执行。记住它们——这将为你节省数小时的调试时间。
1. System.exit() — 紧急终止 JVM
\n\n最著名的案例。如果在 try 块中调用 System.exit(0),JVM 会立即终止,finally 不会执行。
public class FinallyAndExit {\n public static void main(String[] args) {\n try {\n System.out.println("开始工作...");\n System.exit(0); // JVM 在此处终止\n } finally {\n // 这段代码不会执行!\n System.out.println("你以为我会执行?其实不会!");\n }\n }\n}\n\n输出:
\n开始工作...\n\n为什么?因为 System.exit() 在操作系统层面停止了虚拟机。这不是异常——这是“死亡”命令。finally 块根本来不及执行。
System.exit(),请停止。这是一种不良风格。如需结束程序,请使