系列前面验证过 finally 里的 return 会真实吞掉异常。try-with-resources 的隐式 close() 调用面对同样的场景,处理方式完全不一样——真实验证它到底是怎么做的。
上一篇:泛型可变参数的堆污染:真实复现一次编译器拦不住的 ClassCastException
try-with-resources 括号里声明的多个资源,关闭时应该按什么顺序——真实运行观察打开和关闭的日志顺序。
TryWithResourcesDemo.java try (Res a = new Res("A", false); Res b = new Res("B", false); Res c = new Res("C", false)) { System.out.println("try 块内部执行中..."); }
打开 A 打开 B 打开 C try 块内部执行中... 关闭 C 关闭 B 关闭 A
真实结果是逆序关闭(先声明的最后关):C 先关、然后 B、最后 A。这个顺序符合直觉——后声明的资源往往依赖先声明的资源(比如先开文件再包一层缓冲流),关闭时理应先关"外层"的、再关"内层"的,这样任何一个资源的 close() 内部逻辑用到更早声明的资源时,那个资源还真实处于可用状态。
如果 try 主体抛了异常,两个资源的 close() 也各自真实抛了异常,调用方最终能看到几个异常、分别是什么?
TryWithResourcesDemo.java try (Res a = new Res("A", true); Res b = new Res("B", true)) { throw new IllegalStateException("try 块主体抛出的异常"); } catch (Exception e) { Throwable[] suppressed = e.getSuppressed(); // ... }
打开 A 打开 B 关闭 B 关闭 A catch 到的主异常 = try 块主体抛出的异常 真实附带的 suppressed 异常个数 = 2 suppressed: B 关闭时抛出的异常 suppressed: A 关闭时抛出的异常
真实结果和 finally 里裸写 return/throw 完全不同:调用方 catch 到的是 try 主体抛出的那个"最原始"的异常(通常也是最有诊断价值的那个,因为它发生在问题的源头),两个 close() 真实抛出的异常都没有丢失,而是通过 e.getSuppressed() 真实挂在了主异常身上——顺序是先 B 后 A,正好是真实的关闭顺序。
每一步都是真实运行这段代码时打印出的顺序,不是按规范描述手工排的。