因为 fileinputstream 实现了 autocloseable 接口,jvm 在 try 块退出时自动调用 close() 方法;资源必须在 try 括号内声明并初始化,否则无法接管生命周期,且 null 资源不触发 close。

为什么 try-with-resources 能自动关闭 FileInputStream
因为 FileInputStream 实现了 AutoCloseable 接口,JVM 在 try 块退出(无论正常结束还是抛异常)时会自动调用其 close() 方法。不用手动写 finally 块,也不依赖 GC 回收文件句柄。
正确写法:资源声明必须在 try 括号内,且不能赋值给外部变量
常见错误是把 FileInputStream 先 new 出来再传进 try,这样 JVM 无法接管生命周期:
// ❌ 错误:资源未在 try() 中声明,不会自动关闭
FileInputStream fis = new FileInputStream("data.txt");
try {
// ...
} catch (IOException e) {
// ...
}
正确方式如下:
// ✅ 正确:声明 + 初始化都在 try() 内
try (FileInputStream fis = new FileInputStream("data.txt")) {
int b;
while ((b = fis.read()) != -1) {
System.out.write(b);
}
} catch (IOException e) {
// 异常处理
}
- 资源变量作用域仅限于 try 块内,不可在 catch 或 finally 中访问
- 多个资源用分号隔开:
try (FileInputStream fis = ...; BufferedInputStream bis = ...) - 若构造函数抛异常(如文件不存在),
close()不会被调用——因为对象根本没创建成功
异常抑制(Suppressed Exception)会影响错误排查吗
当 try 块抛出异常,且 close() 也抛异常时,后者会被“抑制”并附加到主异常上。不主动检查,容易漏掉真正的问题根源:
- 主异常可通过
e.getSuppressed()获取被抑制的异常数组 -
FileInputStream.close()通常不会抛异常(除非底层文件系统异常),但包装类如BufferedInputStream可能因 flush 失败而抛IOException - 日志框架或 IDE 默认只打印主异常堆栈,需显式打印 suppressed 异常才能发现 close 阶段问题
和传统 try-finally 相比,性能或行为有差异吗
行为上完全等价,编译后都会生成 finally 块调用 close();性能差异可忽略,但语义更清晰、代码更少:
- 编译器会将 try-with-resources 翻译为带
finally的字节码,并确保close()在所有退出路径执行 - 如果资源为
null,不会调用close(),也不会 NPE——但前提是声明时就为 null(如条件初始化),这种写法本身不推荐 - 注意:Java 7+ 才支持,Android minSdkVersion ≥ 19 才可用
最容易被忽略的是资源初始化失败时的边界情况:构造异常发生,资源未创建,自然无须关闭;但若你在外层还做了其他资源分配(比如手动 new 了 byte[] 缓冲区),这部分仍需自行清理。











