应使用try-with-resources自动关闭资源,它能确保资源释放且不掩盖主异常;若手动关闭,需在finally中用独立try-catch处理close()异常,避免抛出或覆盖原始异常。

在 Java 中关闭文件流时捕获并处理异常,核心原则是:**不能让关闭失败的异常掩盖业务逻辑中更重要的异常,同时要确保资源被释放**。推荐使用 try-with-resources 语句,它自动调用 close() 并智能处理异常压制(suppression),这是最安全、简洁的方式。
优先用 try-with-resources(Java 7+)
该语法会自动在 try 块结束时调用资源的 close() 方法,即使发生异常也会执行,并将关闭过程中抛出的异常“压制”到主异常的 getSuppressed() 中,避免覆盖原始异常。
- 资源类必须实现
AutoCloseable接口(FileInputStream、BufferedReader等都满足) - 多个资源可用分号分隔,按声明逆序关闭
- 如果 try 块中抛出异常 A,且 close() 抛出异常 B,则最终抛出的是 A,B 会被添加为 suppressed exception
try (FileInputStream fis = new FileInputStream("data.txt");
BufferedReader reader = new BufferedReader(new InputStreamReader(fis))) {
String line = reader.readLine();
// 处理数据...
} catch (IOException e) {
System.err.println("读取或关闭时出错:" + e.getMessage());
// 可选:检查被压制的异常
for (Throwable suppressed : e.getSuppressed()) {
System.err.println("关闭时被压制的异常:" + suppressed);
}
}
手动关闭时需嵌套 try-catch 或 finally
若因兼容旧版本等原因无法用 try-with-resources,应在 finally 块中关闭,并单独捕获关闭异常,避免影响主异常传播。
- 不要在 finally 中直接 throw 异常(可能丢失原始异常)
- 建议在 finally 中用独立 try-catch 包裹
close() - 若 close() 抛异常,通常只需记录日志,不向上抛出(除非业务要求)
FileInputStream fis = null;
try {
fis = new FileInputStream("data.txt");
// 读取操作...
} catch (IOException e) {
System.err.println("读取失败:" + e.getMessage());
throw e;
} finally {
if (fis != null) {
try {
fis.close();
} catch (IOException e) {
System.err.println("关闭流时失败,已忽略:" + e.getMessage());
// 一般不 re-throw,避免掩盖上面的异常
}
}
}
注意 close() 本身可能抛出 IOException
很多流的 close() 方法会刷新缓冲区并检查底层 I/O 状态,因此也可能触发 IOException(例如磁盘满、网络中断、文件被其他进程锁定等)。
立即学习“Java免费学习笔记(深入)”;
- 不能假设
close()一定成功或静默失败 - 尤其对
OutputStream和带缓冲的流(如BufferedWriter),close() 是唯一保证写出全部数据的机会 - 若业务对数据完整性敏感(如日志写入、事务文件生成),应检查 close() 是否成功,并做重试或告警
避免常见错误
- ❌ 在 try 块里关闭流后,又在 finally 里重复关闭(导致
IOException或NullPointerException) - ❌ 在 catch 中关闭流,但没处理 finally 中的关闭 —— 一旦 try 中异常,流就泄漏
- ❌ 忽略 close() 的返回值或异常,误以为“关了就行”,实际可能数据未落盘
- ❌ 使用 Apache Commons IO 的
IOUtils.closeQuietly()时,完全丢弃异常信息,不利于问题排查


















