必须对每个流的close()单独try-catch并判空,确保异常不中断其他资源释放;关闭顺序应为子资源到父资源;finally中抛异常会掩盖主异常,需记录日志;优先使用try-with-resources,仅在java 6以下或非autocloseable资源时才手动关闭。

finally里每个流都得单独try-catch,不是啰嗦,是防中断
关闭多个流时,如果只用一个大try包裹所有close(),一旦第一个流关闭失败(比如抛出IOException),后续流的close()就根本不会执行——Java不会因为前一个语句异常就跳过后面的代码,但若没加catch,异常会直接冲出finally块,导致整个清理流程提前终止。
正确做法是对每个资源的close()各自套一层try-catch:
- 确保一个流关闭出错,不影响其他流释放
- 避免因未捕获异常导致连接、文件句柄等持续占用
- 每个close()调用都可能抛出检查异常(如IOException、SQLException),语法上必须处理
不判空就调close(),可能触发NullPointerException
流变量通常在try外声明并初始化为null,实际创建可能在try内。如果构造过程失败(如文件不存在导致FileInputStream抛异常),该变量仍为null。此时若不检查直接调close(),就会NPE。
所以每个关闭前必须先判空:
- if (br != null) { try { br.close(); } catch (...) {...} }
- 顺序也重要:一般先关子资源(如BufferedReader),再关父资源(如FileReader)
- 数据库场景同理:ResultSet → Statement → Connection
finally里抛异常会掩盖原始错误,日志比吞异常更关键
如果在finally中close()抛出异常,而你又没捕获或只是e.printStackTrace(),这个新异常会覆盖try块中原本更重要的业务异常(比如SQL执行失败、数据解析错误),导致问题排查困难。
安全做法是:
- 必须用try-catch捕获close()异常
- 不要throw出去,也不要静默忽略
- 至少记录warn或error日志,带上资源类型和异常堆栈
现代写法:优先用try-with-resources,finally手动关是兜底方案
Java 7+起,所有实现AutoCloseable的流(FileInputStream、Connection、Scanner等)都支持try-with-resources。它自动按逆序调用close(),且能压制关闭异常,不干扰主异常传播。
只有两种情况才退回finally手动关:
- 项目还运行在Java 6或更老版本
- 资源不实现AutoCloseable,或需在close后执行额外清理逻辑(如清缓存、发通知)
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











