Java 9 允许 try-with-resources 直接引用已有 AutoCloseable 变量,前提是变量为 final 或事实上 final;该语法由 JLS 第14.20.3节修订支持,兼容性要求编译器目标版本不低于9。

Java 9 的 try-with-resources 允许引用已有变量了
Java 9 确实放宽了 try-with-resources 的语法限制:不再强制要求资源必须在 try 括号里声明,只要它是 AutoCloseable 类型、且作用域可见、非 null,就能直接引用。
这解决了一个很实际的问题——比如你已经有个打开的 FileInputStream 变量,之前只能再包一层 try 或手动 close(),现在可以直接复用:
FileInputStream fis = new FileInputStream("data.txt");
try (fis) { // ✅ Java 9+ 合法
// 使用 fis
} catch (IOException e) {
// 处理异常
}
注意:fis 必须是 final 或“事实上 final”(即声明后没被重新赋值),否则编译报错 variable is not effectively final。
为什么 Java 8 不行,而 Java 9 能支持这种写法
根本原因是 Java 9 修改了 JLS(Java 语言规范)第 14.20.3 节,把资源定义从“局部变量声明”扩展为“资源引用表达式”。编译器不再只看声明位置,而是检查表达式是否指向一个有效的 AutoCloseable 实例。
立即学习“Java免费学习笔记(深入)”;
这种改动不改变语义:资源仍会在 try 块退出时按逆序调用 close(),异常抑制逻辑(addSuppressed)也完全一致。
但要注意兼容性:
- Java 8 及更早版本会直接编译失败,报错
error: resource specification not allowed here - 即使项目用 Java 11 编译,若
source和target设为 8,该语法仍不被接受 - IDE(如 IntelliJ)可能默认按项目 SDK 解析语法,但构建工具(Maven/Gradle)才是最终决定者
try 括号里混用声明和引用的常见错误
Java 9 允许括号内既有新声明、也有已有变量引用,但顺序和作用域容易出错:
BufferedReader reader = Files.newBufferedReader(Paths.get("a.txt"));
try (reader; // ✅ 引用已有变量
InputStream is = new FileInputStream("b.bin")) { // ✅ 新声明
// ...
}
但下面这些会失败:
-
try (reader; reader)—— 重复引用同一变量,编译报错duplicate resource -
try (reader; OutputStream os = null)——os为null时,close()不会被调用,但也不会抛 NPE;不过语义混乱,不推荐 -
try (reader) { reader = null; }—— 虽然语法合法(reader是 effectively final),但close()仍会调用,只是对null调用会抛NullPointerException(如果该类没做空检查)
性能和可读性上,引用已有变量真有优势吗
没有运行时性能差异——字节码层面,Java 9 对引用变量的处理和 Java 8 对声明变量的处理最终都生成类似的 finally 块和 close() 调用。
但可读性和维护性要看场景:
- 适合:资源生命周期跨多个逻辑块(比如先校验再处理),避免重复
open/close开销 - 不适合:变量名含义模糊(如
res)、或在try前已被部分使用并修改状态(比如已读取前几字节) - 特别注意:如果变量在
try外被其他线程共享,引用它进try-with-resources后,close()会真实关闭底层资源,其他线程后续操作会失败(如IOException: Stream closed)
这个细节很多人在迁移旧代码时忽略——不是语法能不能用的问题,而是“关不关、什么时候关、谁还依赖它”要通盘想清楚。










