JDK 1.7 Throwable `addSuppressed()` 方法
好吧,我打通了相关问题,我阅读了JDK 1.7的源代码,但没有找到答案.
在这个问题中,我想完全忽略 fillInStackTrace
.
从 JDK 1.4 开始,添加了 initCause()
方法.例如,当您使用核心反射调用方法时,您会收到 InvocationTargetException,原因是其中包含目标异常.
当我看到这个功能时,我也开始在这样的场景中使用它
尝试 {//包含一些可以抛出新IOException()的代码;}捕捉(IOException e){抛出新的 RuntimeException(e);}
所以,我捕获了一个异常,我还没有准备好在这里处理它,我重新抛出了新的异常,因为我有原始异常作为原因.在某些情况下不是 RuntimeException,而是使用了我的自定义异常,所以有时我也会调用 e.getCause()
以便在外部块中正确处理此异常.
这是 JDK 1.7 之前的情况.为什么以及何时应该使用 addSuppressed()
?我应该将上面的代码更改为
尝试 {//包含一些可以抛出新IOException()的代码;}捕捉(IOException e){RuntimeException re=new RuntimeException(e.getMessage());re.addSuppressed(e);重新投掷;}
作为一个额外的问题,为什么 addSuppressed()
不返回 Throwable
就像 initCause()
允许 throw (RuntimeException)new RuntimeException().initCause(e);
?比如为什么我做不到?:
尝试 {//包含一些可以抛出新IOException()的代码;}捕捉(IOException e){throw (RuntimeException)new RuntimeException(e.getMessage()).addSuppressed(e);}
我将答案提取到单独的帖子中.
解决方案一般来说,当我们以某种方式并行执行时,应该使用 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/ 例如或这里 什么是 Java 7 try-with-resources 字节码等效使用 try-catch-finally?.简而言之,在某种意义上,您可以并行拥有 2 个 Throwable,通常来自 try 块和 finally 块.旧的 try-catch 语义将从 finally 块返回异常,而 suppressed 来自 try 块的异常(或从 catch 块重新抛出异常).新的 try-with-resource 功能使您能够同时获得异常.更重要的是,您将收到原始异常,其中 finally 块中的异常将被抑制
<块引用>请注意,当一个异常导致另一个异常时,通常会捕获第一个异常,然后抛出第二个异常作为响应.换句话说,这两个例外之间存在因果关系.相反,在某些情况下,可以在同级代码块中引发两个独立的异常,特别是在 try-with-resources 语句的 try 块和关闭资源的编译器生成的 finally 块中.在这些情况下,只能传播引发的异常之一.在 try-with-resources 语句中,当有两个这样的异常时,将传播源自 try 块的异常,并将 finally 块的异常添加到由 try 块的异常抑制的异常列表中.当一个异常展开堆栈时,它可以累积多个被抑制的异常.
示例:
公共类 TestClass {静态类 ResourceA 实现 AutoCloseable{公共无效读取()抛出异常{throw new Exception("ResourceA 读取异常");}@覆盖公共无效关闭()抛出异常{throw new Exception("ResourceA 关闭异常");}};静态类 ResourceB 实现 AutoCloseable{公共无效读取()抛出异常{throw new Exception("ResourceB 读取异常");}@覆盖公共无效关闭()抛出异常{throw new Exception("ResourceB 关闭异常");}};//一个测试方法公共静态无效测试()抛出异常{尝试 (ResourceA a = new ResourceA();//ResourceB b = new ResourceB()) {a.read();//面包();} 捕捉(异常 e){扔 e;}}公共静态 void main(String[] args) 抛出异常 {测试();}
}
输出如下:
线程main"java.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 安全 = System.getSecurityManager();如果(安全!= null){security.checkPermission(new RuntimePermission("closeClassLoader"));}列出<IOException>错误 = ucp.closeLoaders();//现在关闭所有剩余的流.同步(可关闭){设置<可关闭>键 = closeables.keySet();for (Closeable c : keys) {尝试 {c.close();} 捕捉(IOException ioex){错误.add(ioex);}}closeables.clear();}if (errors.isEmpty()) {返回;}IOException firstex = errors.remove(0);//抑制任何剩余的异常对于(IOException 错误:错误){**firstex.addSuppressed(错误);**}扔第一个;}
一般来说,这种方法可以用于批处理作业(批量操作),当我们应该继续下一个项目(在本例中关闭下一个打开的流)时,无论操作是否在当前项目成功与否.这样一来,正如我之前所说,我们以某种方式并行执行会产生异常,即抑制.在这种情况下,我们应该使用上面的方法来抛出异常,并在其中保留被抑制的异常.
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.
相关文章