必须先判空再单独try-catch关闭每个流,否则null调用触发nullpointerexception,且未捕获的close异常会掩盖主异常并中断后续关闭;jdk7+优先用try-with-resources自动处理。

在 Java 中,finally 块中关闭 IO 流的关键是:先判空再关闭,且每个流单独 try-catch,避免因前一个流关闭异常导致后续流未关闭。JDK 7+ 更推荐用 try-with-resources,但理解 finally 的正确写法仍很重要。
为什么不能直接在 finally 里无脑 close()
如果流对象为 null(比如构造时就抛出异常),直接调用 close() 会触发 NullPointerException;如果 close() 自身抛出 IOException,又没捕获,可能掩盖 try 块中的真正异常,甚至导致后续流无法关闭。
finally 中安全关闭的写法
按以下原则编写:
- 每个流变量声明为 null(如 FileInputStream fis = null;)
- 在 finally 块中对每个流分别判空 + 单独 try-catch
- 关闭顺序建议与打开顺序相反(后开先关),尤其涉及包装流时(如 BufferedInputStream 包装 FileInputStream)
示例:
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) {}
}
}
更优解:优先使用 try-with-resources
它自动处理判空和异常抑制,语义清晰、代码简洁,且能保证所有资源按逆序关闭:
- 资源需实现 AutoCloseable 接口(所有标准 IO 流都满足)
- 多个资源用分号隔开,JVM 会按声明逆序关闭
- 即使 try 块或 close() 抛异常,首个异常会被抛出,其余异常被抑制(可通过 getSuppressed() 获取)
等效写法:
try (FileInputStream fis = new FileInputStream("a.txt");
BufferedInputStream bis = new BufferedInputStream(fis)) {
// 读取操作...
} catch (IOException e) {
// 异常处理
}
// fis 和 bis 在此处已自动关闭
常见误区提醒
- 不要把多个 close() 塞进同一个 try 块 —— 一个失败会导致后面跳过
- 不要在 close() 后继续使用流 —— 即使没报错,行为未定义
- 包装流(如 BufferedReader)关闭时会自动关闭底层流,无需重复关闭底层(但显式关闭也不报错)
- Apache Commons IO 的 IOUtils.closeQuietly() 可简化判空 + 忽略异常逻辑,适合老项目
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











