JDK 1.7 Throwable`addSuppressed()`方法

时间:2012-01-20 19:14:50

标签: exception jvm java java-7

好吧,我通过相关问题,阅读了JDK 1.7的源代码,但我找不到答案。

在这个问题中,我想完全忽略fillInStackTrace

自JDK 1.4 initCause()方法添加之后。例如,当您使用核心反射来调用该方法时,您会收到InvocationTargetException,其中包含具有目标异常的原因。

当我看到这个功能时,我也开始在这样的场景中使用它

    try {
        //contains some code that can throw new IOException();
    }
    catch(IOException e){
        throw new RuntimeException(e);
    }

所以,我抓住了一个异常,我还没有准备好在这里处理它并且我重新抛出新异常,其中我有原始异常作为原因。在一些有趣的不是RuntimeException,但我使用了自定义异常,所以有时我也会调用e.getCause()以便在外部块中正确处理此异常。

这是JDK 1.7之前的情况。为什么以及何时应该使用addSuppressed()?我应该将上面的代码更改为

    try {
        //contains some code that can throw new IOException();
    }
    catch(IOException e){
        RuntimeException re= new RuntimeException(e.getMessage());
        re.addSuppressed(e);
        throw re;
    }

作为一个额外的问题,为什么不addSuppressed() Throwable initCause()作为throw (RuntimeException)new RuntimeException().initCause(e);允许 try { //contains some code that can throw new IOException(); } catch(IOException e){ throw (RuntimeException)new RuntimeException(e.getMessage()).addSuppressed(e); } ?例如,我为什么不能这样做?:

{{1}}

我将答案提取到另一个帖子。

2 个答案:

答案 0 :(得分:14)

一般情况下,应该使用Throwable addSuppressed()方法,以某种方式执行 parallel 执行,这会产生异常,被抑制。我找到了2个例子;

  • 当调用代码看到原始异常(在try或catch块中)和finally块中发生的异常时,尝试使用资源块(try-finally块)。

  • 批处理作业(批量操作)我们应该继续下一个项目,无论当前项目的操作是否成功

在了解详细信息之前,正如@sarelbotha所说,在我的情况下,我只需要将原始异常包装为我新异常的原因。

try-finally块中的默认行为,其中我们有2个例外,原始异常是被抑制,我们只看到来自finally块的异常。如果我们按顺序使用finally块来关闭资源而不是我们真正希望看到原始异常,但是我们也希望看到来自finally块的异常,这会关闭我们的资源并失败。

  

从版本7开始,该平台支持抑制异常的概念(与try-with-resources语句一起使用)。在堆栈跟踪下方打印出为了传递异常而被抑制的任何异常。

http://docs.oracle.com/javase/7/docs/api/java/lang/Throwable.html#printStackTrace%28%29

首先应该阅读有关try-with-resource的新功能。您可以在此处http://www.baptiste-wicht.com/2010/08/java-7-try-with-resources-statement/或此处What is the Java 7 try-with-resources bytecode equivalent using try-catch-finally?阅读。简而言之,在某种意义上,你可以并行地使用2个Throwable,通常来自你尝试阻止和你的finally块。旧的try-catch语义将从try块中的finally块whule suppress 异常中返回异常(或者从catch块中重新抛出异常)。新的try-with-resource功能使您可以获得两个异常。更重要的是,您将收到原始异常,其中来自finally块的异常将被抑制

  

请注意,当一个异常导致另一个异常时,通常会捕获第一个异常,然后在响应中抛出第二个异常。换句话说,两个例外之间存在因果关系。相反,在某些情况下,可以在兄弟代码块中抛出两个独立的异常,特别是在try-with-resources语句的try块和编译器生成的finally块中,它关闭资源。在这些情况下,只能传播一个抛出的异常。在try-with-resources语句中,当存在两个此类异常时,将传播源自try块的异常,并将finally块中的异常添加到由try块中的异常抑制的异常列表中。作为异常展开堆栈,它可以累积多个抑制异常。

示例:

    public class TestClass {
static class ResourceA implements AutoCloseable{
      public void read() throws Exception{
        throw new Exception("ResourceA read exception");
      }
      @Override
      public void close() throws Exception {
        throw new Exception("ResourceA close exception");
      }
    };

static class ResourceB implements AutoCloseable{
      public void read() throws Exception{
        throw new Exception("ResourceB read exception");
      }
      @Override
      public void close() throws Exception {
        throw new Exception("ResourceB close exception");
      }
    };

    //a test method
    public static void test() throws Exception{
      try (ResourceA a = new ResourceA();
           //ResourceB b = new ResourceB()
              ) {
        a.read();
        //b.read();
      } catch (Exception e) {
        throw e;
      }
    }

    public static void main(String[] args)  throws Exception {
        test();
    }

}

输出如下:

Exception in thread "main" java.lang.Exception: ResourceA read exception
at TestClass$ResourceA.read(TestClass.java:6)
at TestClass.test(TestClass.java:29)
at TestClass.main(TestClass.java:39)
Suppressed: java.lang.Exception: ResourceA close exception
    at TestClass$ResourceA.close(TestClass.java:10)
    at TestClass.test(TestClass.java:31)
    ... 1 more

批处理作业(批量操作)。 好吧,我在try-with-resources之外找到了一些这种方法的用法。下面是来自java.net.URLClassLoader.close

的源代码
 public void close() throws IOException {
    SecurityManager security = System.getSecurityManager();
    if (security != null) {
        security.checkPermission(new RuntimePermission("closeClassLoader"));
    }
    List<IOException> errors = ucp.closeLoaders();
    // now close any remaining streams.
    synchronized (closeables) {
        Set<Closeable> keys = closeables.keySet();
        for (Closeable c : keys) {
            try {
                c.close();
            } catch (IOException ioex) {
                errors.add(ioex);
            }
        }
        closeables.clear();
    }
    if (errors.isEmpty()) {
        return;
    }
    IOException firstex = errors.remove(0);
    // Suppress any remaining exceptions
    for (IOException error: errors) {
        **firstex.addSuppressed(error);**
    }
    throw firstex;
}

一般情况下,这种方法可用于批处理作业(批量操作),当我们应该继续下一个项目(在本例中关闭下一个打开的流)时,无论操作是否在当前项目是否成功。以这种方式,我们之前已经说过,以某种方式 parallel 执行可以产生异常,其中被抑制。在这种情况下,我们应该使用上面的方法来抛出异常,并在其中保留被抑制的异常。

答案 1 :(得分:1)

如果在finally块中执行的代码抛出异常,则会保存禁止的异常。这可能是您不关心的例外情况。在Java 6中,finally块中的这种异常将成为您的调用代码将看到的唯一异常,但是使用新的try-with-resource块,您的调用代码将看到原始异常,并且虚拟finally块中发生的异常将是在getSuppressed()。

在您的情况下,只需将原始异常包装为新异常的原因。