关于java:最终使用时抑制异常消失了吗?

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及更早版本中那样运行,并且不使用新引入的addSuppressed(..) getSuppressed()功能。这就是它不像你期望的那样行为的原因。

我认为你所期望的行为也不符合逻辑。我希望它表现得像这样:

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的兼容性,所以我猜这就是为什么它不像那样。