【Java 开发日记】finally 释放的是什么资源?
这是一个非常经典且容易被误解的问题。很多人会脱口而出:“finally 就是用来释放资源的啊!”——但严格来说,finally 本身并不“释放”任何资源。
finally 真正做的是:提供一个“无论如何都会执行”的代码块,而资源释放的代码通常写在 finally 里面。
核心一句话
finally 块的典型用途是:放置资源清理代码,确保那些需要手动关闭/释放的系统资源(如文件、连接、锁等)在 try 块正常结束、return、break、continue、throw 异常等任何退出路径下都能被执行。
finally 最常用来释放/清理的资源类型
| 资源类型 | 典型对象/接口 | 释放方式(常见写法) | 为什么必须在 finally 里做? |
|---|---|---|---|
| 文件流 | FileInputStream, FileOutputStream, BufferedReader, BufferedWriter 等 | close() | 不关闭会导致文件句柄泄漏、系统资源耗尽 |
| 数据库连接 | java.sql.Connection, Statement, ResultSet | close() | 连接池耗尽、数据库压力大 |
| 网络连接 | Socket, ServerSocket, HttpURLConnection 等 | close() | 端口占用、连接泄漏 |
| JDBC 相关 | PreparedStatement, CallableStatement 等 | close() | 同数据库连接 |
| 锁(手动锁) | ReentrantLock | unlock() | 死锁风险 |
| 其他系统资源 | Scanner(老代码中常见)、RandomAccessFile 等 | close() | 底层依赖文件描述符 |
| 自定义资源 | 一些需要手动释放的业务资源(如临时文件句柄、缓存引用等) | 自定义 cleanup 方法 | 防止内存/资源泄漏 |
代码示例(传统写法):
FileInputStream fis = null;
try {
fis = new FileInputStream("data.txt");
// 读取文件...
} catch (IOException e) {
e.printStackTrace();
} finally {
if (fis != null) {
try {
fis.close(); // 真正的资源释放动作在这里
} catch (IOException e) {
e.printStackTrace();
}
}
}
Java 7 之后:try-with-resources(强烈推荐)
从 Java 7 开始,绝大部分需要关闭的资源都实现了 AutoCloseable 或 Closeable 接口,这时应该优先使用 try-with-resources,它会自动调用 close(),代码更简洁、更安全。
try (FileInputStream fis = new FileInputStream("data.txt");
BufferedInputStream bis = new BufferedInputStream(fis)) {
// 使用 fis / bis
// 不需要手动 close
} catch (IOException e) {
e.printStackTrace();
}
// 这里 fis 和 bis 已经自动关闭了
try-with-resources 底层其实相当于在 finally 里自动调用了 close(),而且支持多个资源、异常抑制(suppressed exceptions)等高级特性。
常见误区与澄清
| 误区 | 真相 |
|---|---|
| finally 专门负责释放资源 | 错!finally 只是保证执行,释放资源是我们自己写在 finally 里的代码 |
| 所有资源都要在 finally 里释放 | 错!实现了 AutoCloseable 的资源优先用 try-with-resources |
| finally 里抛异常会覆盖 try/catch 的异常 | 正确,但 Java 7+ 用 suppressed 机制记录了原始异常(getSuppressed()) |
| finally 一定执行 | 几乎一定,但有极少数极端情况不会执行(如 JVM 被 kill、System.exit()、死循环、电源断电) |
现代 Java 开发建议(2025–2026 视角)
- 优先使用 try-with-resources(99% 的场景)
- 老代码/不支持 AutoCloseable 的资源才用传统 try-catch-finally + close()
- 永远不要在 finally 里做业务逻辑,只做清理工作
- ReentrantLock 这种手动锁也推荐写在 finally 里 unlock(虽然有 try-with-resources 风格的 Lock,但不常见)
- 日志记录可以在 finally 里做,但要小心别抛异常
一句话总结:
finally 本身不释放资源,它释放的是“开发者写在 finally 里的清理代码”,而这些清理代码通常是
close()、unlock()、dispose()等动作,用来归还系统资源(文件句柄、数据库连接、socket、锁等)。
希望这篇日记把 finally 和资源释放的关系说清楚了。如果你还有疑问(比如 suppressed 异常怎么看、多个资源的关闭顺序、try-with-resources 的字节码实现等),随时问,我继续展开!