異常分爲:
Exception : 運行時異常,我們需要捕獲並且處理,讓程序自己恢復正常運行。
Error : 編譯時異常(程序本身無法處理的異常:系統崩潰,虛擬機報錯,內存不足,內存溢出)。
直接上代碼:
這裏看到可以catch到的是Exception異常。有一些編譯時可能會發生的異常也會提示我們throws拋出或者捕獲,否則無法通過編譯。
一步步來,先看看Exception和Error到底是個什麼東西。
Exception:發現繼承自Throwable
Error:(也是繼承自Throwable)
所以Error和Exception兩個異常類都繼承自Throwable。這裏借網上一張關係圖:
知道了這些基本概念後,開始看編譯時的字節碼文件長個什麼樣,其中還包含了很多意想不到的小驚喜。
先編譯並且查看字節碼文件
其他操作指令介紹在我另外一篇博客,這裏直奔主題!
彩蛋1:
這裏有個很有趣的地方,明明代碼裏面只有一個finally,爲什麼這裏有三個finally???那就是一個面試必問點,finally是異常機制中必定執行的語句塊,在JVM字節碼指令中爲了確保它一定被執行,所有地方都會加一段這個finally要執行的代碼。
(1)第一個地方,假設沒有異常,正常執行後也執行finally裏面的輸出
(2)第二個地方。有異常,捕獲處理完後,也執行finally裏面的輸出
(3)第三個地方。有異常並且沒有捕獲到,因爲我們定義的運行時異常很多,如果指定的Type類型不對應catch到的,或者是Throwable父類一些特殊異常,那就沒辦法正確處理,所以這一個finally都是用來捕獲漏網之魚的。
所以!finally在字節碼中有三份!
彩蛋2:
改一下代碼!
結果爲什麼是10086呢?不是try中沒有異常,返回了a = 1 嗎?難道是多個return 覆蓋了? 其實根據上面的分析,finally是一定會執行的,所以在第一次return 1 的時候,它沒有真正返回,只是進行了一個附值操作而已,直到finally重新修改了 a = 10086 ,再return。下面看字節碼:
{
public com.company.Main();
descriptor: ()V
flags: ACC_PUBLIC
Code:
stack=1, locals=1, args_size=1
0: aload_0
1: invokespecial #1 // Method java/lang/Object."<init>":()V
4: return
LineNumberTable:
line 3: 0
public static void main(java.lang.String[]);
descriptor: ([Ljava/lang/String;)V
flags: ACC_PUBLIC, ACC_STATIC
Code:
stack=2, locals=4, args_size=1
0: new #2 // class com/company/Main$Father
3: dup
4: invokespecial #3 // Method com/company/Main$Father."<init>":()V
7: astore_1
8: iconst_0
9: istore_2
10: aload_1
11: iload_2
12: invokevirtual #4 // Method com/company/Main$Father.get:(I)I
15: istore_3
16: getstatic #5 // Field java/lang/System.out:Ljava/io/PrintStream;
19: iload_3
20: invokevirtual #6 // Method java/io/PrintStream.println:(I)V
23: return
LineNumberTable:
line 6: 0
line 7: 8
line 8: 10
line 9: 16
line 10: 23
}
這裏也就是說,finally沒有執行,這個方法都沒有彈出棧,所以不停地iload只是單純壓入操作數棧表,finally執行完了之後,才istore_3,pop出棧。同時有個奇怪的地方,就是異常athrow操作指令消失了!
這也是小彩蛋,正常來說我們catch到的Exception error,在這一步處理的時候,如果finally有return,那麼會發生吞噬異常的情況,也就是拋出的異常不見了。
彩蛋3:
這裏的異常,在字節碼中都是有一個ExceptionTable存放的。很簡單明瞭易懂:
再改一下代碼,讓它catch多個異常類型:
直奔主題看字節碼文件中的異常表:
解釋:
From : 從第幾行開始檢測
To :到第幾行結束
Target : 假如From和To中的代碼執行發生異常跳到哪一行處理
Type : 就是我剛纔定義的全部異常類型。
Type 爲 any 的情況,就是拋出了捕獲不到的類型。所以全部跳到71行去處理。類似於現在爆發的從未出現過的新型冠狀病毒。
71行就是我們的finally語句塊。這裏是最後一道防線,打印出來給程序員處理。同時讓程序繼續運行。