JDK 1.7 可抛出的'addSuppressed()'方法

2022-09-03 06:33:37

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

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

从JDK开始,添加了1.4方法。例如,当您使用核心反射调用方法时,您将收到 InvocationTargetException,其中包含其中的目标异常的原因。initCause()

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

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

所以,我抓住了一个异常,我还没有准备好在这里处理它,我重新抛出新的异常,因为我有原始的异常作为原因。在某些情况下,不是 RuntimeException,而是使用我的自定义异常,因此有时我也调用 to 以在外部块中正确处理此异常。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()ThrowableinitCause()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

一般来说,当我们以某种方式并行执行时,应该使用Storable方法,这可以产生异常,在抑制的地方。我发现了2个例子;addSuppressed()

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

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

在详细介绍之前,如前所述,就@sarelbotha,就我而言,我只需要继续将原始异常包装为新异常的原因。

try-finally block 中的默认行为,其中我们有 2 个异常,即原始异常被抑制,我们只看到来自 finally 块的异常。如果我们使用 finally block on order 来关闭资源,那么我们真正希望看到原始异常,但可以选择性地,我们也希望看到来自 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/ 或此处 使用try-catch-finally的Java 7 try-with-resources字节码等效物是什么?简而言之,从某种意义上说,你可以并行2个可投掷的,通常是从你尝试块和从你的最终块。旧的 try-catch 语义将返回来自 try 块的最终块 whule 抑制异常的异常(或从 catch 块中重新抛出异常)。新的资源试用功能使您能够同时获得这两个异常。更重要的是,您将收到原始异常,其中来自最终块的异常将被抑制

请注意,当一个异常导致另一个异常时,通常会捕获第一个异常,然后引发第二个异常作为响应。换句话说,这两种例外之间存在因果关系。相反,在某些情况下,可以在同级代码块中引发两个独立的异常,特别是在 try-with-resources 语句的 try 块和编译器生成的最终块中,该块关闭了资源。在这些情况下,只能传播一个引发的异常。在 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

批处理作业(批量操作)。好吧,我在尝试资源之外发现了这种方法的一些用法。下面,是来自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;
}

通常,这种方法可以在批处理作业(批量操作)中使用,当我们应该继续下一个项目(如本例中所示关闭下一个打开的流)时,无论当前项目的操作是否成功。通过这种方式,正如我之前所说,我们以某种方式并行执行,这可能会产生异常,在抑制的情况下。在这种情况下,我们应该使用上面的方法来引发异常,其中包含剩余的抑制异常。


答案 2

如果在最终块中执行的代码引发异常,则会保存禁止的异常。这是一个你可能不关心的例外。在Java 6中,最终块中的这种异常将成为您的调用代码将看到的唯一异常,但是使用新的资源试用块,您的调用代码将看到原始异常,而在虚拟最终块中发生的异常将在getSuppressed()中。

在您的情况下,只需继续包装原始异常作为新异常的原因即可。