finally是确保文件句柄硬性释放的可靠时机,因其在try执行后必运行(除system.exit或jvm崩溃外),可显式调用close()避免资源泄漏;推荐优先使用try-with-resources自动管理。

在流式数据传输(如文件读取、网络响应体消费)中,finally 子句是确保物理文件句柄被硬性释放的关键机制——它不依赖于是否发生异常,也不受 return、break 或线程中断影响,只要 try 块开始执行,finally 就一定会运行。
为什么 finally 是解绑句柄的可靠时机
Java 中的 FileInputStream、FileOutputStream 等资源持有操作系统级文件句柄,JVM 不保证及时回收。即使对象被 GC 回收,句柄也可能滞留,导致“Too many open files”错误。而 finally 提供确定性执行点,可在此显式调用 close(),避免资源泄漏。
-
try中发生异常 →catch处理后仍会进入finally -
try中正常执行并return→return的值先暂存,finally执行完再返回 -
try中遇到System.exit()或 JVM 崩溃 →finally不执行(这是唯一例外)
典型写法:手动在 finally 中 close
适用于 Java 7 之前或需精细控制关闭逻辑的场景:
FileInputStream fis = null;
try {
fis = new FileInputStream("data.bin");
byte[] buf = new byte[8192];
int len;
while ((len = fis.read(buf)) != -1) {
// 处理流数据,例如写入网络 socket
outputStream.write(buf, 0, len);
}
} catch (IOException e) {
log.error("传输中断", e);
} finally {
if (fis != null) {
try {
fis.close(); // 真正释放 OS 句柄
} catch (IOException ignored) {
// close 可能抛异常,但不应干扰主流程,通常忽略
}
}
}
更安全的替代:try-with-resources(推荐优先使用)
Java 7+ 引入的 try-with-resources 本质是编译器自动将资源声明转为隐式 finally 关闭,语义等价但更简洁、不易出错:
- 资源必须实现
AutoCloseable接口(FileInputStream等均已实现) - 无论正常结束还是异常退出,编译器生成的字节码都会在
finally块中调用close() - 多个资源按声明逆序关闭(后声明的先关),避免依赖冲突
示例:
try (FileInputStream fis = new FileInputStream("data.bin");
OutputStream out = socket.getOutputStream()) {
byte[] buf = new byte[8192];
int len;
while ((len = fis.read(buf)) != -1) {
out.write(buf, 0, len);
}
out.flush();
} catch (IOException e) {
log.warn("流传输失败", e);
}
// fis 和 out 在此处已 guaranteed closed
注意 close() 本身可能抛异常的处理
某些流(如 BufferedOutputStream)的 close() 会强制刷缓冲区,可能触发底层 I/O 异常。若发生在 finally 中,需防止掩盖原始异常:
- 在手动
finally中,用嵌套try-catch捕获close()异常并忽略或记录,不 throw 出去 - 在
try-with-resources中,若try块已抛异常,close()抛的异常会被抑制(suppressed),可通过Throwable.getSuppressed()获取 - 关键业务中,若关闭失败意味着数据未落盘,应单独判断并告警(如
FileOutputStream关闭失败可能表示磁盘满)
不复杂但容易忽略:句柄解绑不是“等 GC”,而是靠代码明确触发;finally 或 try-with-resources 是达成这一目标最直接、最可控的方式。











