且构网

分享程序员开发的那些事...
且构网 - 分享程序员编程开发的那些事

JDK 1.7 Throwable`addSuppressed()`方法

更新时间:2022-10-14 23:05:29

一般来说,应该使用Throwable addSuppressed()在某种程度上,我们具有可以产生异常的并行执行,即被抑制。我发现了2个例子;




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


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




在了解详细信息之前,如@sarelbotha所述,在我的情况下,我只需要保持原始异常作为我的新例外的原因。



try-finally块中的默认行为,我们有2个异常,原始异常被抑制我们只看到finally块的异常。如果我们使用finally block来关闭资源,而不是真正想要看到原始的异常,但是我们也可以看到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个Throwable,通常来自于您尝试阻止和从您的finally块。一个旧的try-catch语义将从try块(或从catch块重新抛出异常)的finally块whule suppress 异常返回异常。一个新的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异常{
throw new Exception(ResourceA close exception);
}
};

static class ResourceB实现AutoCloseable {
public void read()throws异常{
throw new Exception(ResourceB read exception);
}
@Override
public void close()throws异常{
throw new Exception(ResourceB close exception);
}
};

//一个测试方法
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();
}

}



输出将如下:

 线程中的异常mainjava.lang.Exception:ResourceA读取异常
TestClass $ ResourceA.read(TestClass.java:6)
在TestClass.test(TestClass.java:29)
在TestClass.main(TestClass.java:39)
禁止:java。 lang.Exception:ResourceA关闭异常
在TestClass $ ResourceA.close(TestClass.java:10)
在TestClass.test(TestClass.java:31)
... 1更多

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


  public void close()throws IOException {
SecurityManager security = System.getSecurityManager();
if(security!= null){
security.checkPermission(new RuntimePermission(closeClassLoader));
}
列表< IOException> errors = ucp.closeLoaders();
//现在关闭任何剩余的流。
synchronized(可关闭){
Set< Closeable> keys = closeables.keySet();
(关闭c:keys){
try {
c.close();
} catch(IOException ioex){
errors.add(ioex);
}
}
closeables.clear();
}
if(errors.isEmpty()){
return;
}
IOException firstex = errors.remove(0);
//禁止任何剩余的异常
(IOException错误:错误){
** firstex.addSuppressed(error); **
}
throw firstex;
}


一般来说,可以使用这种方法在批处理作业(批量操作)中,当我们应该继续下一个项目(在本例中关闭下一个打开的流),而不管当前项目的操作是否成功。以这种方式,我们以前所述,以某种方式执行可能产生异常的执行,即被抑制。在这种情况下,我们应该使用上面的方法抛出异常,其中包含剩余的禁止异常。


Well, I get through related questions, I read the source code of JDK 1.7, but I don't find the answer.

In this question I want to completely ignore fillInStackTrace.

As of JDK 1.4 initCause() method was added. For example, when you use core reflection to invoke the method you receives InvocationTargetException with the cause that have target exception in it.

When I saw this feature I started to use it also in a scenario like this

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

So, I catch an exception, I am not ready to deal with it here and I rethrow new exception where I have original exception as the cause. In some scenarious not RuntimeException, but my custom exception is used, so sometimes I also call to e.getCause() in order to properly handle this exception in the outer block.

This is situation in pre JDK 1.7. Why and when should I use addSuppressed()? Should I change the code above to

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

And as a bonus question why doesn't addSuppressed() return Throwable as initCause() does to allow throw (RuntimeException)new RuntimeException().initCause(e);? For example why can't I do?:

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

I extracted the answer to a separate post.

In general, Throwable addSuppressed() method should be used when in some way we have parallel execution which can produce exception, that where suppressed. I found 2 examples;

  • Try-with-resource block (try-finally block) when the calling code would see the original exception (in the try or catch block) and the exception that happened in the finally block.

  • batch jobs (bulk operations) when we should proceed to the next item regardless whether the operation on the current item succeeded or not

Before getting to the details, as @sarelbotha stated, in my case I just have to keep wrapping the original exception as the cause of my new exception.

Default behaviour in try-finally block, where we have 2 exceptions, that the original exception is suppressed and we see only exception from finally block. If we use finally block on order to close the resource than we really want to see the original exception, but optionally we want to see also exceptions from the finally block, that closed our resource and fails.

As of release 7, the platform supports the notion of suppressed exceptions (in conjunction with the try-with-resources statement). Any exceptions that were suppressed in order to deliver an exception are printed out beneath the stack trace.

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

First one should read about try-with-resource new feature. You can read it here http://www.baptiste-wicht.com/2010/08/java-7-try-with-resources-statement/ for example or here What is the Java 7 try-with-resources bytecode equivalent using try-catch-finally?. In short, you can have 2 Throwable in parallel in some sense, typically from you try block and from your finally block. An old try-catch semantic will return exception from the finally block whule suppressed exception from the try block (or rethrowing exception from the catch block). A new try-with-resource feature enables you to get both exception. Even more, you will receive original exception where exception from the finally block will be suppressed

Note that when one exception causes another exception, the first exception is usually caught and then the second exception is thrown in response. In other words, there is a causal connection between the two exceptions. In contrast, there are situations where two independent exceptions can be thrown in sibling code blocks, in particular in the try block of a try-with-resources statement and the compiler-generated finally block which closes the resource. In these situations, only one of the thrown exceptions can be propagated. In the try-with-resources statement, when there are two such exceptions, the exception originating from the try block is propagated and the exception from the finally block is added to the list of exceptions suppressed by the exception from the try block. As an exception unwinds the stack, it can accumulate multiple suppressed exceptions.

Example:

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

}

Output will be the following:

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

batch jobs (bulk operations). Well, I found some usage of this method outside try-with-resources. Below, is source code from the 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;
}

In general, such approach can be used in batch jobs (bulk operations), when we should proceed to the next item (closing next open streams as is in this example) regardless whether the operation on the current item succeeded or not. In such a way, we have as I stated before, in some way parallel execution which can produce exception, that where suppressed. In such cases we should use the approach above to throw on exception with remaining suppressed exception in it.