学习使用Java7新语法try-with-resources,在查看编译文件时,接触到addSuppressed()方法。
addSuppressed()是什么?
传递被抑制的异常,便于开发者找到bug报错点。
特此记录测试理解过程:
资源类:【特意加了异常,以便测试】
public class Panda implements AutoCloseable{
public Panda(){
System.out.println("Panda was born");
}
@Override
public void close() throws Exception {
System.out.println("Panda is closed");
throw new Exception("panda.close() 的异常");
}
public void play() throws Exception {
System.out.println("Panda is playing");
throw new Exception("panda.play() 的异常");
}
}
使用try-with-resources的测试类:
public class Test {
public static void main(String[] args) throws Exception {
try (Panda panda = new Panda()) {
panda.play();
System.out.println("Over");
} catch (Exception e) {
throw new Exception(e);
} finally {
System.out.println("Finally!");
}
}
}
测试结果:
Panda was born
Panda is playing
Panda is closed
Finally!
Exception in thread "main" java.lang.Exception: java.lang.Exception: panda.play() 的异常
at com.example.Test.main(Test.java:13)
Caused by: java.lang.Exception: panda.play() 的异常
at com.example.Panda.play(Panda.java:21)
at com.example.Test.main(Test.java:10)
Suppressed: java.lang.Exception: panda.close() 的异常
at com.example.Panda.close(Panda.java:16)
at com.example.Test.main(Test.java:12)
如结果所示,play()时报错,“Over”不打印,依旧会执行close(),最后执行finally块代码,“Finally”打印。这是try-with-resources语法的特性。
查看测试类的class文件:
public class Test {
public Test() {
}
public static void main(String[] args) throws Exception {
try {
Panda panda = new Panda();
Throwable var2 = null;
try {
panda.play();
} catch (Throwable var20) {
var2 = var20;
throw var20;
} finally {
if (panda != null) {
if (var2 != null) {
try {
panda.close();
} catch (Throwable var19) {
var2.addSuppressed(var19);
}
} else {
panda.close();
}
}
}
} catch (Exception var22) {
throw new Exception(var22);
} finally {
System.out.println("Finally!");
}
}
}
try-with-resources语法底层还是try-catch-finally,那么和传统的try-catch-finally方法又有什么区别呢?
区别就是第21行的addSuppressed()方法,用传统的try-catch-finally来比对测试下:
public class Test {
public static void main(String[] args) throws Exception {
Panda panda = null;
try {
panda = new Panda();
panda.play();
System.out.println("Over");
} catch (Exception e) {
throw new Exception(e);
} finally {
if (panda != null) {
panda.close();
}
System.out.println("Finally");
}
}
}
测试结果:
Panda was born
Panda is playing
Panda is closed
Exception in thread "main" java.lang.Exception: panda.close() 的异常
at com.example.Panda.close(Panda.java:16)
at com.example.Test.main(Test.java:18)
依结果可见,play()抛了异常,没有打印“over”,之后又执行了finally块的close(),这里也抛了异常,所以“Finally”也未打印,且控制台只打印了close()的异常。
以上可得,除了代码执行顺序之外,两者最大差别就是异常的显隐问题了。这就是addSuppressed()的作用。那么其原理为何呢?
我们来分析下:
传统的try-catch-finally代码:
catch抛出play()产生的异常A,然后代码控制权跳转到finally块,close()又产生新异常B,然后控制权跳转调用栈的上一层方法直接抛出异常B,抑制屏蔽了异常A。
那么addSuppressed()呢?先来看看此方法的注释:
Appends the specified exception to the exceptions that were suppressed in order to deliver this exception.【将指定的异常附加到为传递此异常而抑制的异常上。】
这翻译很拗口,通俗点讲,就是在被抑制的play()的异常A上 附加close()的异常B 一起传递出去。
为什么AB的关系是这样呢?
当然是为了第一眼找到报错的位置呀~
例如:上面try-with-resources测试类的报错
Exception in thread "main" java.lang.Exception: java.lang.Exception: panda.play() 的异常
at com.example.Test.main(Test.java:13)
Caused by: java.lang.Exception: panda.play() 的异常
at com.example.Panda.play(Panda.java:21)
at com.example.Test.main(Test.java:10)
Suppressed: java.lang.Exception: panda.close() 的异常
at com.example.Panda.close(Panda.java:16)
at com.example.Test.main(Test.java:12)
至于为什么close()异常前用了Suppressed【被抑制的】,这个就不知道了~
查看源码:
public final synchronized void addSuppressed(Throwable exception) {
if (exception == this)
throw new IllegalArgumentException(SELF_SUPPRESSION_MESSAGE, exception);
if (exception == null)
throw new NullPointerException(NULL_CAUSE_MESSAGE);
if (suppressedExceptions == null) // Suppressed exceptions not recorded
return;
if (suppressedExceptions == SUPPRESSED_SENTINEL)
suppressedExceptions = new ArrayList<>(1);
suppressedExceptions.add(exception);
}
不是特别能看懂,就大概知道原来最后得到的是一个list,所以所有的异常都被抛出了。
至此,对于Throwable.addSuppressed()方法有了一个清晰的认识。