JDK 1.7 Throwable`addSuppressed()`方法


问题内容

好吧,我解决了相关问题,阅读了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);
    }

我将答案提取到另一篇文章中。


问题答案:

一般而言,addSuppressed()当我们以某种方式 并行 执行会产生异常的情况时,应使用Throwable 方法 ,该情况会 被抑制
。我发现了两个例子;

  • 当调用代码将看到原始异常(在try或catch块中)以及在finally块中发生的异常时,使用try-with-resource块(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

第一个应该阅读有关“尝试使用资源”新功能的信息。您可以在这里阅读它,例如http://www.baptiste-
wicht.com/2010/08/java-7-try-with-resources-statement/或在这里阅读Java 7 try-with-resources字节码等效于什么最后尝试赶上?。简而言之,在某种意义上,您可以并行地具有2 Throwable,通常是从您的try块和您的finally块中获得。旧的try-
catch语义将返回finally块中的异常,而try块中的已 抑制
异常(或catch块中的异常抛出)。新的尝试资源功能使您可以同时获得这两种异常。甚至,您将收到原始异常,其中来自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 errors = ucp.closeLoaders();
// now close any remaining streams.
synchronized (closeables) {
Set 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;
}

通常,这种方法可用于 批处理作业
(批量操作)中,无论何时对当前项目的操作成功与否,我们都应继续进行下一个项目(如本例所示,关闭下一个打开的流)。以这种方式,正如我之前所说,在某种程度上,
并行 执行会产生异常,而这种异常会被 抑制 。在这种情况下,我们应该使用上面的方法引发异常并保留其中抑制的异常。