Suppressed exception disappeared when using finally?
这是代码。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 | public class TestTest { public static void main (String[] args) throws Exception { try { run(); } catch(Exception e) { printSuppressedExceptions(e); } } public static void printSuppressedExceptions(Throwable t) { System.out.println(t); System.out.println("suppressed exceptions:" + t.getSuppressed().length); } public static void run() throws Exception { try(MyResource r = new MyResource("resource");) { System.out.println("try"); System.getProperty("").length(); // throws illegalArgumentException } catch(Exception e) { printSuppressedExceptions(e); throw e; } finally { new MyResource("finally").close(); } } } class MyResource implements AutoCloseable { private final String name; public MyResource(String name) { this.name = name; } @Override public void close() throws Exception { throw new Exception("exception" +" from" + this.name); } } |
由于try块抛出的异常抑制了资源异常,我首先得到了"抑制异常:1",这是可以理解的。 但是当最后抛出异常时,似乎所有被抑制的异常都消失了,因为我得到了"java.lang.Exception:来自finally的异常",接着是"抑制异常:0",我认为它应该是1。
我浏览了Java教程,但它肯定会说
However, in this example, if the methods readLine and close both throw exceptions, then the method readFirstLineFromFileWithFinallyBlock throws the exception thrown from the finally block; the exception thrown from the try block is suppressed.
来自try-with-resources声明
怎么会发生?
这是执行您期望的代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 | public class TestTest { public static void main (String[] args) throws Exception { try { run(); } catch(Exception e) { printSuppressedExceptions(e); } } public static void printSuppressedExceptions(Throwable t) { System.out.println(t); System.out.println("suppressed exceptions (" + t.getSuppressed().length +"):"); for (Throwable suppressed : t.getSuppressed()) { System.out.println(" -" + suppressed); } } public static void run() throws Exception { Exception exceptionFromCatch = null; try(MyResource r = new MyResource("resource");) { System.out.println("try"); System.getProperty("").length(); // throws illegalArgumentException } catch(Exception e) { exceptionFromCatch = e; printSuppressedExceptions(e); throw e; } finally { try { new MyResource("finally").close(); } catch (Exception e) { if (exceptionFromCatch!=null) { e.addSuppressed(exceptionFromCatch); } throw e; } } } } class MyResource implements AutoCloseable { private final String name; public MyResource(String name) { this.name = name; } @Override public void close() throws Exception { throw new Exception("exception" +" from" + this.name); } } |
因此,让我们通过代码的try-with-resource部分(如JDK 1.7.0中所介绍的那样),看看会发生什么(请参阅使用try-catch-finally进行什么是Java 7 try-with-resources字节码等效?细节):
-
执行try-with-resource块
MyResource r = new MyResource("resource") -
执行try块并抛出
IllegalArgumentException -
try-with-resource块为所有资源调用
close() (在您的示例中只有一个) -
close() 抛出异常,但由于try块中的异常具有优先级,因此close() 抛出的异常被抑制并通过addSuppressed(..) 添加
因此,该部分的工作方式与您阅读本教程时的预期相同
现在是代码的try-catch-finally部分(如在JDK 1.6及更早版本中):
-
执行try块并抛出
IllegalArgumentException - (catch块的行为与没有catch块的行为相同)
- 执行finally块并抛出异常
- finally块中的异常具有优先级,并且try块中的异常被抑制
但是这次在java教程中使用的单词抑制并不代表"被压制并添加到实际抛出的异常",而是"被压制并丢失为必杀技"。因此它仍然像在JDK 1.6及更早版本中那样运行,并且不使用新引入的
我认为你所期望的行为也不符合逻辑。我希望它表现得像这样:
1 2 3 4 5 6 7 8 9 10 11 12 13 | ... } finally { try { new MyResource("finally").close(); } catch (Exception e) { if (exceptionFromCatch!=null) { exceptionFromCatch.addSuppressed(e); } else { throw e; } } } ... |
这将始终优先考虑try块中的异常(使用新的try-with-resource功能实现),并将catch块中的异常添加到列表中。但这会破坏与JDK 1.6的兼容性,所以我猜这就是为什么它不像那样。