多变量资源释放时关闭顺序严格按声明逆序执行:后声明的先关闭,先声明的后关闭。JVM强制此机制以确保依赖资源安全释放,如BufferedInputStream须在FileInputStream之后声明,否则底层流提前关闭会导致上层close()异常;资源必须独立完整声明,不支持赋值或复用;关闭异常作为suppressed exception附加到主异常上;推荐按“外层→内层”声明以契合依赖层级。

多变量资源释放时,声明顺序直接决定关闭顺序:后声明的先关,先声明的后关。这不是可选行为,而是JVM强制执行的机制,关系到资源能否安全释放、是否抛出意外异常。
关闭顺序是逆序,不是按使用逻辑
即使你用 BufferedInputStream 包装 FileInputStream,它们之间存在依赖,JVM 也不会按“谁用谁”来关,而是严格按括号里写的顺序取反:
- 写成
try (FileInputStream fis = ...; BufferedInputStream bis = ...)→ 先关bis,再关fis - 反过来写
try (BufferedInputStream bis = ...; FileInputStream fis = ...)→ 先关fis,再关bis(这会出问题)
包装类的 close() 通常会触发底层流的刷新和基础关闭;如果底层流先关了,上层再调 close() 就可能抛 IOException 或静默失败。
资源必须独立声明,不能复用或赋值
try 括号里只接受完整的变量声明语句,不接受表达式或已有变量引用:
- ✅ 正确:
FileInputStream fis = new FileInputStream("a.txt") - ❌ 错误:
fis = new FileInputStream("a.txt")(缺类型和关键字) - ❌ 错误:
FileInputStream fis1, fis2 = ...(不支持多变量声明) - ❌ 错误:
try (fis; BufferedInputStream bis = ...)(不能把已有变量放进去)
异常会被抑制,但主逻辑异常仍保留
如果 try 块里已抛出一个异常(比如读文件时的 IOException),而某个资源的 close() 又抛出另一个异常(比如网络断开导致的 SocketException),后者不会覆盖前者:
- 主异常照常抛出
- 关闭异常作为 suppressed exception 附加到主异常对象上
- 可通过
exception.getSuppressed()拿到所有被抑制的异常
实际建议:按“外层→内层”顺序声明
对应资源的依赖层级,把高层包装器放在后面声明,底层通道放在前面:
- 文件读取:先
FileInputStream,再BufferedInputStream,最后InputStreamReader - 数据库操作:先
Connection,再PreparedStatement,最后ResultSet - 网络通信:先
Socket,再BufferedWriter(包装其getOutputStream())
这样既能满足依赖安全,又让代码意图一目了然。

















