关闭顺序由try括号内声明顺序的逆序决定:后声明的先关闭,先声明的后关闭;资源必须独立声明,不可复用变量或嵌套创建,否则无法自动关闭。

在变量流链式调用中使用 AutoCloseable 资源时,关闭顺序由 声明顺序的逆序 决定,与“谁包装谁”或“谁依赖谁”的逻辑无关,只看你在 try 括号里怎么写。
声明顺序决定关闭先后
比如包装流场景:
try (FileInputStream fis = new FileInputStream("a.txt"); BufferedInputStream bis = new BufferedInputStream(fis))
这里 bis 在后声明,所以先关闭;fis 在先声明,所以后关闭。输出一定是:
Closing: FileInputStream
这恰好符合包装流的安全要求:外层流关完,再让底层流关——避免外层还在用却提前释放了底层数据源。
不能复用变量或跳过独立声明
以下写法是错误的,编译不通过:
-
FileInputStream fis = new FileInputStream("a.txt"); try (fis; BufferedInputStream bis = new BufferedInputStream(fis))——fis不是声明式初始化 -
try (var fis = new FileInputStream("a.txt"); var bis = new BufferedInputStream(fis); var dis = new DataInputStream(bis))—— 看似链式,但每个变量仍需独立、完整初始化
资源必须是“一行一声明”,不可用已有变量赋值,否则 JVM 无法识别其生命周期边界,也就无法插入自动 close() 调用。
异常发生时的关闭保障
即使 try 块中抛出异常,所有已成功初始化的资源仍会按逆序执行 close():
- 若
fis初始化成功,bis初始化失败(如构造时抛IOException),则只有fis会被关闭 - 若两者都初始化成功,但
try块内抛异常,则bis.close()先执行,再执行fis.close() - 如果
close()本身也抛异常,它会被“抑制”并附加到主异常的getSuppressed()列表中,不会掩盖原始业务异常
手动链式创建 ≠ 自动管理链式依赖
注意:new BufferedInputStream(new FileInputStream(...)) 这种表达式本身只是对象创建方式,不代表资源管理关系。JVM 不解析嵌套构造,只认 try 括号中显式列出的变量。所以:
- 你不能只写
try (var bis = new BufferedInputStream(new FileInputStream("x"))) {...}—— 底层FileInputStream没有独立变量名,不会被自动关闭,造成泄漏 - 正确做法始终是把每一层可关闭资源单独列出来,哪怕它们物理上是嵌套创建的

















