異常 Java虛擬機異常使用Throwable或其子類的實例來表示,拋異常本質上是程式控制權的一種即時的、非局部的轉換,即從拋出的地方轉換至處理異常的地方。 導致異常的原因 執行了athrow位元組碼指令。 虛擬機同步檢測到程式發生了非正常的執行情況,這 ...
異常
Java虛擬機異常使用Throwable或其子類的實例來表示,拋異常本質上是程式控制權的一種即時的、非局部的轉換,即從拋出的地方轉換至處理異常的地方。
導致異常的原因
- 執行了athrow位元組碼指令。
- 虛擬機同步檢測到程式發生了非正常的執行情況,這時的異常必將緊接著在發生非正常執行情況的位元組碼指令之後拋出。例如:
由Java虛擬機執行的每個方法都會配有零至多個異常處理器。異常處理器描述了其在方法代碼中的有效作用範圍、能處理的異常類型以及處理異常的代碼位置。
如果當前方法中沒有找到任何異常處理器,並且當前方法調用期間確實發生了異常,那當前方法的操作數棧和局部變數表都將被丟棄,隨後它對應的棧幀出棧,並恢復到該方法調用者的棧幀中。未被處理的異常將在方法調用者的棧幀重新被拋出,併在整個方法調用鏈里不斷重覆進行前面描述的處理過程。如果已經達到方法調用鏈頂端,卻還沒有找到合適的異常處理器去處理這個異常,那整個執行線程都將被終止。
搜索異常處理器時的搜索順序很關鍵,在class文件里,每個方法的異常處理器都存儲在一個表中。運行時,當有異常拋出後,Java虛擬機按照class文件中的異常處理器表所描述的異常處理器的先後順序,從前至後進行搜索。
Java虛擬機本身不會對方法的異常處理器表進行排序或者其他方式的強制處理,所以Java語言中對異常處理的語義,實際上是通過編譯器適當安排異常處理器在表中的順序來協助完成的。
try-catch-finally 的例子
public class Test {
void testException(int i) {
try {
System.out.println(i);
} catch (NullPointerException e) {
throw new NullPointerException();
} catch (RuntimeException e) {
throw new RuntimeException();
} finally {
System.err.print(i);
}
}
}
對上面的例子進行編譯javac Test.java
和反編譯javap -c Test.class
,生成位元組碼命令。
0: getstatic #2 // 獲取第一個靜態方法 println:Field java/lang/System.out:Ljava/io/PrintStream;
3: iload_1
4: invokevirtual #3 // 執行第一個靜態方法 println:Method java/io/PrintStream.println:(I)V
7: getstatic #4 // 獲取finally的靜態方法 print:Field java/lang/System.err:Ljava/io/PrintStream;
10: iload_1
11: invokevirtual #5 // 執行finally的靜態方法 print:Method java/io/PrintStream.print:(I)V
14: goto 45 // 跳到return指令結束程式
==========================
17: astore_2
18: new #6 // class java/lang/NullPointerException
21: dup
22: invokespecial #7 // Method java/lang/NullPointerException."<init>":()V
25: athrow
26: astore_2
27: new #8 // class java/lang/RuntimeException
30: dup
31: invokespecial #9 // Method java/lang/RuntimeException."<init>":()V
34: athrow
35: astore_3
36: getstatic #4 // Field java/lang/System.err:Ljava/io/PrintStream;
39: iload_1
40: invokevirtual #5 // Method java/io/PrintStream.print:(I)V
43: aload_3
44: athrow
==========================
45: return
Exception table: // 異常表,順序和源碼一致
from to target type
0 7 17 Class java/lang/NullPointerException //如果0-7之間有NullPointerException異常,跳到17
0 7 26 Class java/lang/RuntimeException //如果0-7之間有RuntimeException異常,跳到26
0 7 35 any //0-7之間任意情況下都會到35行,即finally
17 36 35 any //17-36之間任意情況下都會到35行,即finally
參考資料:《深入理解Java虛擬機(第二版)》《Java虛擬機規範(Java SE 8版)》