最后使用时,被抑制的异常消失了吗?

时间:2016-11-09 09:36:51

标签: java exception try-with-resources

这是代码。

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块抛出的异常抑制了资源异常,我得到了#34;抑制异常:1"起初这是可以理解的。但是当最后抛出异常时,似乎所有被抑制的异常都消失了,因为我得到了#34; java.lang.Exception:来自最终的异常"其次是"抑制异常:0"我认为它应该 1 。 我浏览了Java教程,它肯定会说

  

但是,在此示例中,如果方法readLine并关闭两个抛出异常,则方法readFirstLineFromFileWithFinallyBlock将抛出finally块抛出的异常;从try块抛出的异常被抑制。

来自The try-with-resources Statement

怎么可能发生?

1 个答案:

答案 0 :(得分:1)

以下代码可以满足您的期望:

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中所介绍的那样),看看会发生什么(有关详细信息,请参阅What is the Java 7 try-with-resources bytecode equivalent using try-catch-finally?):

  • 执行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教程中使用的 抑制 这个词并不代表"被抑制并添加到实际抛出的异常中#34; "被压制并迷失为天堂" 。因此它仍然像在JDK 1.6及更早版本中那样运行,并且不使用新引入的addSuppressed(..) getSuppressed()功能。这就是它没有像你期望的那样表现的原因。

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

...
        } 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的兼容性,所以我想这就是为什么它不会表现得那样。