防止Java IO流资源泄漏的关键是确保流一定被关闭,无论读取是否成功或是否异常;必须显式关闭所有外部资源型流;优先使用try-with-resources自动管理,手动关闭时需在finally中判空并单独捕获异常。

Java IO 流读取结束后,防止资源泄漏的关键不是“等读取完再关”,而是**确保流一定被关闭,无论读取是否成功、是否中途异常、甚至构造流就失败**。核心在于资源生命周期的确定性管理,而非依赖操作逻辑的完整性。
必须关闭所有外部资源型流
凡是涉及文件、网络、管道等系统资源的流(如 FileInputStream、SocketInputStream、BufferedReader、PreparedStatement),都必须显式释放。不关闭会导致:
- Windows 下文件被锁定,无法删除或重命名
- Linux/macOS 中文件描述符耗尽,抛出
IOException: Too many open files - 数据库连接池连接泄漏,最终服务不可用
优先用 try-with-resources 自动保障关闭
这是 JDK 7+ 的标准做法,编译器自动生成安全的 finally 关闭逻辑,无需手动判空或嵌套 try-catch。
- 资源必须实现
AutoCloseable(所有标准 IO 类都满足) - 声明在
try (…)括号内,作用域清晰,超出即触发close() - 多个资源用分号分隔,JVM 按声明逆序关闭(如包装流先关,底层流后关)
- 即使
read()抛异常,或close()自身抛异常,主异常仍向上抛出,关闭异常被抑制(可通过getSuppressed()查看)
示例:
立即学习“Java免费学习笔记(深入)”;
try (FileInputStream fis = new FileInputStream("data.bin");
BufferedInputStream bis = new BufferedInputStream(fis);
FileOutputStream fos = new FileOutputStream("copy.bin")) {
int b;
while ((b = bis.read()) != -1) {
fos.write(b);
}
} catch (IOException e) {
// 只处理业务逻辑异常,关闭由 JVM 保证
e.printStackTrace();
}
手动关闭时 finally 必须判空 + 单独捕获
仅在需兼容老版本或特殊控制流时使用。错误写法(直接 close、不判空、共用 try-catch)极易引发 NullPointerException 或掩盖原始异常。
- 每个流变量初始化为
null - 在
finally中逐个判空,再各自包裹try-catch - 关闭顺序建议与打开顺序相反(尤其包装流,如
BufferedInputStream包装FileInputStream,应先关前者)
示例:
立即学习“Java免费学习笔记(深入)”;
FileInputStream fis = null;
BufferedInputStream bis = null;
try {
fis = new FileInputStream("a.txt");
bis = new BufferedInputStream(fis);
// 读取操作
} catch (IOException e) {
// 处理读取异常
} finally {
if (bis != null) {
try { bis.close(); } catch (IOException ignored) {}
}
if (fis != null) {
try { fis.close(); } catch (IOException ignored) {}
}
}
别忽略“流根本没创建成功”的情况
如果 new FileInputStream("xxx") 因文件不存在抛出 FileNotFoundException,fis 仍是 null。此时若在 finally 中未判空就调 close(),会触发 NullPointerException,反而掩盖了原始问题。
- 所有流变量声明时统一初始化为
null - 不要把
close()放到try或catch块里——路径覆盖不全 - 避免在循环体内反复新建/关闭流;应在外层打开,循环内复用


















