try-with-resources 是编译期语法糖,javac 编译为含 finally 的字节码显式调用 close;多资源按“后声明先关闭”顺序关闭;异常时主异常抛出,close 异常被压制并可通过 getsuppressed() 获取;资源变量隐式 final 且作用域限于 try 块内。

面试官考 try-with-resources,很少只问“怎么写”,而是围绕它背后的机制、边界情况和实际踩坑点来设计问题。重点不在语法本身,而在你是否真正理解它“为什么能自动关”、“关错顺序会怎样”、“异常堆叠时谁被抛出”。
考编译器怎么实现的
这是高频开场题。答案不是“JVM自动调用 close”,而是要指出:它本质是编译期语法糖,javac 会把 try-with-resources 编译成带 finally 的等效字节码,里面显式插入了 close() 调用逻辑。
- Java 7+ 编译器会在生成的字节码中,为每个资源生成 try-finally 块,确保无论正常退出还是异常跳出,close 都被执行
- 不会新增 JVM 指令,纯靠编译器重写代码结构实现
- 可以反编译 class 文件验证:javap -c 查看字节码,能看到明显的 finally + invokevirtual close
考多个资源的关闭顺序和依赖关系
常配合代码片段让你判断关闭顺序,或指出错误写法。核心是“后声明先关闭”,尤其当资源有包装关系时(比如 BufferedInputStream 包装 FileInputStream)。
- 必须后声明外层流、先声明底层流,才能保证关闭时先关缓冲流、再关文件流,否则底层流提前关闭会导致外层流 close 失败
- 错误示例:
try (BufferedInputStream bis = new BufferedInputStream(new FileInputStream("a.txt")); FileInputStream fis = new FileInputStream("b.txt"))—— 这里 fis 在 bis 后声明,会先关 fis,但 bis 内部还持着 fis 引用,bis.close() 时可能抛 IOException - 正确写法:把被包装的流写在前面,包装流写在后面
考异常抑制(suppressed exception)机制
这是区分“背过语法”和“真懂异常处理”的关键题。面试官会问:try 块抛异常,close 也抛异常,最终抛出的是哪个?怎么拿到被压制的异常?
- 主异常(try 块里抛的)会被抛出;close 抛的异常会被压制,不覆盖主异常
- 通过
throwable.getSuppressed()获取所有被压制的异常数组 - 注意:只有 close 异常发生在主异常之后才会被压制;如果 try 块没异常,close 异常就正常抛出
- 常见陷阱:catch 住主异常后直接 return 或 throw 新异常,会丢失 suppressed 异常信息
考资源变量的生命周期和赋值限制
容易忽略的细节题,考察是否写过真实代码。
- try() 中声明的资源变量隐式 final,不能在 try 块内重新赋值(如
fis = null),否则编译报错 - 资源作用域仅限于 try 块内,块外不可访问
- Java 9 开始支持“已存在变量”写法:
FileInputStream fis = new FileInputStream("x"); try (fis) { ... },但 fis 仍不可在 try 块内 reassign - 自定义类要支持 try-with-resources,只需实现 AutoCloseable 并提供有意义的 close() 逻辑
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











