事实上,每个throwable都是java.lang.Throwable的一个实例,它隐含在Java字节代码/ JVM的各个地方.即使任何人的处理程序意图表示可能在Throwable类型层次结构之外的东西,该想法也会失败,因为今天的类文件必须具有包含异常处理程序的方法的StackMapTable,并且StackMapTable将引用任何可抛出的作为java.lang的实例. Throwable
1.
即使使用旧类型的推断验证器,一个重新抛出一个可抛出的处理程序也隐含地包含一个断言,即任何可抛出的是java.lang.Throwable的一个实例,因为这是允许抛出唯一的对象.
The objectref must be of type reference and must refer to an object that is an instance of class Throwable or of a subclass of Throwable.
简短的答案:不,不可能有一种情况,除了java.lang.Throwable(或一个子类)的实例以外的东西可以抛出或捕获.
我试图创建一个try-with-resource语句的最小例子来分析javac的输出.结果清楚地表明,该结构是javac在内部工作但不能有意的工件.
示例如下所示:
public static void tryWithAuto() throws Exception {
try (AutoCloseable c=dummy()) {
bar();
}
}
private static AutoCloseable dummy() {
return null;
}
private static void bar() {
}
(我用jdk1.8.0_20编译)
我将异常处理程序表放在生成的字节代码的开始处,以便在查看指令序列时更容易地引用该位置:
Exception table:
from to target type
17 23 26 Class java/lang/Throwable
6 9 44 Class java/lang/Throwable
6 9 49 any
58 64 67 Class java/lang/Throwable
44 50 49 any
现在指示:
开始很简单,使用两个局部变量,一个用于保存AutoCloseable(索引0),另一个用于可能的throwable(索引1,使用null初始化).调用dummy()和bar(),然后检查AutoCloseable为null,看看它是否必须被关闭.
0: invokestatic #2 // Method dummy:()Ljava/lang/AutoCloseable;
3: astore_0
4: aconst_null
5: astore_1
6: invokestatic #3 // Method bar:()V
9: aload_0
10: ifnull 86
如果AutoCloseable不为null,并且第一个奇怪的事情发生,我们到达这里,那个肯定为null的throwable被检查为null
13: aload_1
14: ifnull 35
以下代码将关闭AutoCloseable,由上表中的第一个异常处理程序保护,这将调用addSuppressed.因为在这一点上,变量#1为null这是死码:
17: aload_0
18: invokeinterface #4, 1 // InterfaceMethod java/lang/AutoCloseable.close:()V
23: goto 86
26: astore_2
27: aload_1
28: aload_2
29: invokevirtual #6 // Method java/lang/Throwable.addSuppressed:(Ljava/lang/Throwable;)V
32: goto 86
请注意,死代码的最后一个指令是goto 86,一个返回的分支,所以如果上面的代码没有死代码,我们可以开始想知道为什么会在随后忽略的Throwable上调用addSuppressed.
现在遵循执行的代码,如果变量#1为空(读,总是).它只是调用close和分支到返回指令,而不是捕获任何异常,所以close()抛出的异常传播给调用者:
35: aload_0
36: invokeinterface #4, 1 // InterfaceMethod java/lang/AutoCloseable.close:()V
41: goto 86
现在我们进入第二个异常处理程序,覆盖try语句的正文,声明为捕获Throwable,读取所有异常.它将Throwable存储到变量#1中,如预期的那样,但也将其存储到过时的变量#2中.然后它重新抛出Throwable.
44: astore_2
45: aload_2
46: astore_1
47: aload_2
48: athrow
以下代码是两个异常处理程序的目标.首先,它的目标是多余的任何异常处理程序,涵盖与Throwable处理程序相同的范围,因此,如你所怀疑的,这个处理程序不会做任何事情.此外,它是第四个异常处理程序的目标,捕获任何东西并覆盖上面的异常处理程序,所以我们在稍后的一个指令中捕获指令#48的重新抛出的异常.为了使事情更加有趣,异常处理程序涵盖的不仅仅是上面的处理程序;结束于#50,独家,它甚至涵盖了自己的第一条指令:
49: astore_3
所以第一件事是引入一个第三个变量来保持同样的可抛出.现在AutoCloseable被检查为null.
50: aload_0
51: ifnull 84
现在检查变量#1的throwable为null.只有假设的抛物线不是Throwable,它才可以为null.但是请注意,在这种情况下,验证者将拒绝整个代码,因为StackMapTable将所有变量和操作数堆栈条目声明为将任何可抛出的值与赋值兼容为java.lang.Throwable
54: aload_1
55: ifnull 78
58: aload_0
59: invokeinterface #4, 1 // InterfaceMethod java/lang/AutoCloseable.close:()V
64: goto 84
最后但并非最不重要的是,当一个挂起的异常存在时,我们将处理异常抛出的异常处理程序,它将调用addSuppressed并重新抛出主异常.它引入了另一个局部变量,这表明javac indeed never uses swap甚至在适当的情况下.
67: astore 4
69: aload_1
70: aload 4
72: invokevirtual #6 // Method java/lang/Throwable.addSuppressed:(Ljava/lang/Throwable;)V
75: goto 84
所以以下两个指令只能被调用,如果catch任何可能意味着除了java.lang.Throwable之外的东西,那不是这种情况.代码路径与正常情况连接在#84.
78: aload_0
79: invokeinterface #4, 1 // InterfaceMethod java/lang/AutoCloseable.close:()V
84: aload_3
85: athrow
86: return
所以底线是,任何一个额外的异常处理程序只对#54,#55,#78和#79的四个指令的死码负责,而由于其他原因(#17 – #32)还有更多的死代码,加上一个奇怪的“擒抱”(#44 – #48)代码,这也可能是一个想法的工具,处理任何不同于Throwable.此外,一个异常处理程序的范围覆盖本身,可能与“Strange exception table entry produced by Sun’s javac”有关,为suggested in the comments.
作为一个备注,Eclipse为指令序列只生成60个字节而不是87个代码,只有两个预期的异常处理程序和3个局部变量而不是5个代码.在这个更紧凑的代码中,它处理了可能的情况,即身体抛出的异常可能与一次抛出相同,在这种情况下,addSuppressed不能被调用. javac生成的代码并不在此.
0: aconst_null
1: astore_0
2: aconst_null
3: astore_1
4: invokestatic #18 // Method dummy:()Ljava/lang/AutoCloseable;
7: astore_2
8: invokestatic #22 // Method bar:()V
11: aload_2
12: ifnull 59
15: aload_2
16: invokeinterface #25, 1 // InterfaceMethod java/lang/AutoCloseable.close:()V
21: goto 59
24: astore_0
25: aload_2
26: ifnull 35
29: aload_2
30: invokeinterface #25, 1 // InterfaceMethod java/lang/AutoCloseable.close:()V
35: aload_0
36: athrow
37: astore_1
38: aload_0
39: ifnonnull 47
42: aload_1
43: astore_0
44: goto 57
47: aload_0
48: aload_1
49: if_acmpeq 57
52: aload_0
53: aload_1
54: invokevirtual #30 // Method java/lang/Throwable.addSuppressed:(Ljava/lang/Throwable;)V
57: aload_0
58: athrow
59: return
Exception table:
from to target type
8 11 24 any
4 37 37 any