【问题标题】:JDK 1.7 Throwable `addSuppressed()` methodJDK 1.7 Throwable `addSuppressed()` 方法
【发布时间】:2012-01-20 19:14:50
【问题描述】:

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

我将答案提取到单独的帖子中。

【问题讨论】:

  • 您从相关方法的 javadoc 中没有理解什么?我以前从未见过addSuppressed() 方法,但是快速阅读javadoc 可以清楚地知道它的目的是什么。 (提示,在您的示例用例中,它没有有意义)。
  • 好吧,javadoc 还不够。为什么要否决这个问题?
  • 为什么还不够,有什么不明白的?
  • 我发现这个stackoverflow.com/questions/7860137/… 非常清楚地解释了 try-with-resource 功能是如何实际实现的。
  • @jtahlborn,让投票决定这个问题和答案的有用性。只是提醒你:blog.stackoverflow.com/2011/07/…

标签: exception jvm java java-7


【解决方案1】:

一般来说,当我们以某种方式并行执行可能产生异常的情况下,应使用Throwable addSuppressed()方法,而抑制。我找到了 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,通常来自 try 块和 finally 块。旧的 try-catch 语义将从 finally 块返回异常,而 try 块中的 suppressed 异常(或从 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;
}

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

【讨论】:

  • 我发现这个stackoverflow.com/questions/7860137/… 非常清楚地解释了 try-with-resource 功能是如何实际实现的。
  • catch (Exception e) { throw e; } 在你的第一个代码中真的有必要吗?
【解决方案2】:

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

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

【讨论】:

  • 通过基本上重新发布 javadoc 中的内容,您鼓励人们先发布,然后再思考/从不。
  • 嗯,我不知道 JDK 1.7 中的 try-with-resource 新特性。开始阅读它。谢谢。
  • @jtahlborn,我应该如何理解我应该阅读 printStackTrace() 方法 docs.oracle.com/javase/7/docs/api/java/lang/… ?我现在对堆栈跟踪的东西不感兴趣。至于我,问题之间没有联系。
  • @alexsmail - 嗯?我读到的唯一 javadoc 是 docs.oracle.com/javase/7/docs/api/java/lang/…,它回答了我所有的问题(基本上就是这个答案所包含的内容)。
  • jtahlborn,stackoverflow 的存在是因为没人想要 RTFM。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-11-09
  • 2023-03-17
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-06-08
相关资源
最近更新 更多