事實上,每個throwable是java.lang.Throwable
的一個實例隱含在Java字節代碼/ JVM的各個位置。即使任何處理器註定要可能代表Throwable
類型層次之外的東西,這種想法在今天的類文件必須包含異常處理方法StackMapTable
和StackMapTable
將參考Throwable作爲實例的任何失敗java.lang.Throwable
1。
即使舊類型推理驗證,該處理程序重新拋出一個拋出隱含包含任何拋出是java.lang.Throwable
實例因爲這是被允許拋出的唯一對象athrow
斷言。
http://docs.oracle.com/javase/specs/jvms/se8/html/jvms-6.html#jvms-6.5.athrow
的objectref必須reference
類型,並且必須引用的對象,則Throwable
類或的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
被檢查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
,跳轉至return
因此,如果上面的代碼中並沒有死的代碼嗎,我們可能會開始想知道爲什麼在Throwable
上調用addSuppressed
之後會被忽略。
現在遵循如果變量#1是null
(總是閱讀)執行的代碼。它只是調用close
並分支到return
指令,不獲取任何的異常,所以通過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的拋出被檢查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
因此,如果捕捉任何可能意味着比其他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的指令序列中,具有兩個預期的異常處理程序只和三個局部變量,而不是5。在更緊湊的代碼中,它處理了可能的情況,即由主體引發的異常可能與close
所拋出的異常相同,在這種情況下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
請張貼完整的字節碼。 – 2014-09-02 03:33:52
https://dl.dropboxusercontent.com/u/26793257/example.txt – 2014-09-02 04:13:31
這裏是jimple代碼(由soot生成): https://dl.dropboxusercontent.com/u/26793257/example。 jimple.txt 我在那裏寫了兩條評論來指出相關的行。 – 2014-09-02 04:19:03