本文目录导读:

我们来通过几个生动的案例,手把手实操 finally 的使用。finally 的核心作用是:无论 try 块中是否发生异常,finally 块中的代码都一定会被执行,它通常用于释放资源(如关闭文件、数据库连接、释放锁等)。
重要前提: 只有一种情况 finally 块不会执行:在 try 或 catch 块中执行了 System.exit(0)(强制退出JVM)。
最基本的资源清理(最常用)
场景: 你打开了一个文件(或数据库连接),必须确保关闭它,哪怕中间出错了。
import java.io.*;
public class FinallyDemo1 {
public static void main(String[] args) {
BufferedReader reader = null;
try {
// 1. 尝试打开文件
reader = new BufferedReader(new FileReader("test.txt"));
String line = reader.readLine();
System.out.println("文件内容: " + line);
// 假设这里故意让分母为0,抛出异常
int result = 10 / 0;
} catch (FileNotFoundException e) {
System.out.println("捕获到异常: 文件未找到!");
} catch (IOException e) {
System.out.println("捕获到异常: 读取文件错误!");
} catch (ArithmeticException e) {
System.out.println("捕获到异常: 数学运算错误!");
} finally {
// 2. 无论上面是否出错,finally 确保在这里关闭文件
System.out.println("【finally】正在执行清理工作...");
if (reader != null) {
try {
reader.close();
System.out.println("文件成功关闭。");
} catch (IOException e) {
System.out.println("关闭文件时出错: " + e.getMessage());
}
}
}
System.out.println("程序继续执行(即使发生了异常,只要被catch了)");
}
}
运行结果分析(如果你当前目录下没有 test.txt 文件):
捕获到异常: 文件未找到!
【finally】正在执行清理工作...
程序继续执行(即使发生了异常,只要被catch了)
关键点: 即使在第一个 catch 就捕获了异常并退出了 try 块,finally 依然会在程序继续向下执行之前运行。
即使 try 里有 return,finally 也会执行
场景: 这是一个容易混淆的面试题。try 快中有 return 语句,finally 会在返回之前执行。
public class FinallyReturnDemo {
public static int testFinally() {
int result = 1;
try {
System.out.println("执行 try 块");
// 发生异常,会跳到 catch
Integer.parseInt("abc");
result = 2;
return result; // 这行其实执行不到,因为上面抛异常了
} catch (NumberFormatException e) {
System.out.println("捕获异常: " + e.getMessage());
result = 3;
return result; // 这里有一个return
} finally {
System.out.println("执行 finally 块 (result当前值=" + result + ")");
result = 4; // 修改了result,但注意:返回的是return语句的“快照”
// 如果finally里也有return,会覆盖catch里的return!
}
}
public static void main(String[] args) {
int value = testFinally();
System.out.println("main方法接收到的返回值: " + value);
}
}
运行结果:
执行 try 块
捕获异常: java.lang.NumberFormatException: For input string: "abc"
执行 finally 块 (result当前值=3)
main方法接收到的返回值: 3
深度理解:
catch中的return result;先把result的值(此时是3)保存在一个临时位置。finally中的result = 4;修改了变量,但不会影响已经保存的返回值3。- 特例: 如果你把
finally里的result = 4;改成return 100;,catch里的return会失效,最终返回100。永远不要在 finally 中使用 return!
模拟数据库连接释放(联网操作)
场景: 模拟建立网络连接或数据库连接,必须确保最后关闭连接,防止资源泄露。
public class FinallyConnectionDemo {
// 模拟一个连接
static class Connection {
boolean isClosed = false;
void open() { System.out.println("连接已打开"); }
void sendData(String data) {
// 模拟数据发送失败
throw new RuntimeException("网络中断,发送失败!");
}
void close() {
isClosed = true;
System.out.println("连接已关闭");
}
}
public static void main(String[] args) {
Connection conn = null;
try {
conn = new Connection();
conn.open();
// 发送数据,这里会抛异常
conn.sendData("Hello");
} catch (Exception e) {
System.out.println("捕获到异常: " + e.getMessage());
// 不能让程序崩溃,但连接必须关
} finally {
System.out.println("【finally】确保关闭连接...");
if (conn != null && !conn.isClosed) {
conn.close(); // 这里安全关闭
}
}
System.out.println("程序优雅结束,连接已安全释放。");
}
}
运行结果:
连接已打开
捕获到异常: 网络中断,发送失败!
【finally】确保关闭连接...
连接已关闭
程序优雅结束,连接已安全释放。
没有 finally 的后果: sendData 抛出异常,直接跳出 try 块,conn.close() 永远不会执行,导致连接泄露(最终可能导致系统资源耗尽)。
最佳实操原则(适合面试和写汇报)
- 写在一行? 不,
try-catch-finally是块结构,必须分行写,但你可以压行写try (资源) { ... }这是 try-with-resources,是finally的语法糖,适用于AutoCloseable的资源。 - 什么时候必须用 try-with-resources? 如果你操作的是
InputStream、OutputStream、Connection、Statement、ResultSet这些资源,优先使用 try-with-resources(Java 7+),它能自动调用close(),代码更简洁,且能正确处理隐式异常。// 用 try-with-resources 替代上面的案例一 try (BufferedReader reader = new BufferedReader(new FileReader("test.txt"))) { String line = reader.readLine(); System.out.println(line); } catch (IOException e) { e.printStackTrace(); } // 不需要 finally 来 close(),自动完成 - 数据回滚或状态恢复:
finally也是理想的地方,比如一个操作完成了99%,突然失败了,你不能留下一个半成品状态。finally可以帮你把一些“临时占位”恢复成原样。
总结一句话: 有借有还,才能再借不难;try 借资源,finally 负责还(释放/关闭)。