JDK 1.7 Throwable`addSuppressed()`方法

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

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

从JDK 1.4开始,添加了initCause()方法。 例如,当您使用核心reflection来调用该方法时,您会收到InvocationTargetException,其中包含具有目标exception的原因。

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

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

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

这是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); } 

我把答案提取到一个单独的post。

一般情况下,应该使用Throwable addSuppressed()方法,当我们以某种方式进行并行执行时,可以产生exception, addSuppressed() 抑制的情况 。 我找到了两个例子;

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

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

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

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

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

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

首先应该阅读有关try-with-resource的新function。 你可以在这里阅读http://www.baptiste-wicht.com/2010/08/java-7-try-with-resources-statement/ ,例如或者这里什么是Java 7 try-with-resources字节码等效使用的try-catch-最后? 。 简而言之,在某种意义上,你可以并行使用2 Throwable,通常来自你尝试阻止和你的finally块。 旧的try-catch语义将从finally块中返回exception,从try块中抑制exception(或者从catch块中重新抛出exception)。 新的try-with-resourcefunction使您可以获得两个exception。 更重要的是,您将收到原始exception,其中finally块的exception将被抑制

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

例:

  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; } 

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

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

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