Java I/O 流必须显式关闭以避免“Too many open files”错误,最可靠方式是使用try-with-resources(JDK 7+),它自动在finally块中调用close(),确保资源及时释放且异常处理安全。

Java 中的 Stream 流(如 InputStream、OutputStream、Reader、Writer)本身不是函数式编程里的 java.util.Stream,而是 I/O 资源。它们必须显式关闭,否则会持续占用操作系统文件描述符,最终触发 “Too many open files” 错误。真正关键的不是“终结流”,而是“确保资源被及时释放”。最可靠、最简洁的方式是依赖 JVM 的自动管理机制。
优先使用 try-with-resources(JDK 7+)
这是现代 Java 开发的标准做法,要求资源实现 AutoCloseable 接口(所有标准 I/O 流都满足)。编译器会在字节码中自动生成 finally 块并调用 close(),无论是否发生异常都会执行。
- 资源声明写在
try括号内,多个资源用分号分隔;关闭顺序与声明顺序相反(后声明的先关闭) - 无需手动调用
close(),也不需要额外的finally块或判空逻辑 - 若多个资源关闭时抛出异常,JVM 会将次要异常作为 suppressed exception 附加到主异常上,避免掩盖原始错误
多层嵌套流要统一声明
比如用 BufferedInputStream 包装 FileInputStream,再配合 FileOutputStream 写入——这些都应放在同一个 try 括号里,而不是层层嵌套 try 块。
- 错误示范:外层 try 管
FileInputStream,内层 try 管BufferedInputStream,容易遗漏关闭 - 正确写法:
try (FileInputStream fis = ...; BufferedInputStream bis = new BufferedInputStream(fis); FileOutputStream fos = ...) - 这样能保证所有资源在作用域结束时按序关闭,且异常传播清晰
无法用 try-with-resources 时,务必在 finally 中关闭
某些场景下资源不能在 try 括号中初始化(例如工厂方法返回、条件分支创建),此时只能退回到传统方式,但必须严格遵循规范:
立即学习“Java免费学习笔记(深入)”;
-
close()必须放在finally块中,不能放在try或catch里 - 关闭前检查是否为
null(构造失败时变量可能仍为 null) -
close()本身可能抛IOException,需单独捕获或向上抛出,不可忽略
避免常见误区
有些做法看似合理,实则存在隐患:
- 只在
try块末尾调用close():一旦前面代码抛异常,close()就不会执行 - 用
System.gc()试图回收流:GC 不负责释放底层 OS 句柄,完全无效 - 依赖 finalize() 方法:该方法不保证何时执行,甚至可能永不执行,已被标记为 deprecated
- 认为“小项目不用关”:单次泄漏影响小,但循环操作或高并发下很快耗尽文件描述符限额


















