天天看點

位元組碼層面了解try、catch、finally

​面試中經常有關于try、catch、finally相關的問題,今天從位元組碼層面了解他們的運作流程。

簡單代碼

直接上測試簡單代碼,如下圖:

位元組碼層面了解try、catch、finally

這裡是一個簡單的測試代碼,代碼中有三個異常和一個finally,這裡用一個int數組來替代對象,面試中經常會問最終會傳回數組(對象)的值是多少?

在這裡我們通過jclasslib檢視編譯後的位元組碼,并找到方法的位元組碼指令,如下圖:

位元組碼層面了解try、catch、finally

右邊被圈中的就是test()方法執行的位元組碼指令,位元組碼指令較長,接下來一部分一部分的分析。

位元組碼指令分析

前面30行之前的指令對應的是try-catch中間的代碼,把fileInputStream與serverSocket初始化出來放到方法的變量表中。

接着30行之後的指令如下圖:

位元組碼層面了解try、catch、finally

aload_1加載本地變量表中第1項變量(this是第0項,ids是第1項),接着iconst_0、iconst_1分别表示加載int型0、1,iastore表示把第數組第0項設定值為1,最後astore表示把數組存入本地變量表,也就是在執行"ids[0] =1"這一步代碼。

我們看接下來37至40行指令與上一步比較相似,翻譯過來是在執行"ids[0]=5",41行至49行在執行“System.out.println(“finally code”);”這樣代碼,51行才執行了return;

從上一段分析可以看出,直到finally中的代碼執行完成後才執行了return指令。這樣我們就能回答面試的那個問題了,因為在finally中修改了數組的值!

異常處理

先看看這個方法的異常表,如下圖:

位元組碼層面了解try、catch、finally

異常表中除了我們代碼中指明的三個異常外,編譯器還自動生成了5個Any類型的異常,用于處理其他不可預期的異常處理。圈中的三列分别表示異常監控的指令開始、結束行以及處理行,也就是在對應的指令行出現異常他們就能處理。

可以看到我們指明的異常監控的是try-catch中的指令,而編譯器生成的則是監控異常進行中的指令。

繼續看位元組碼指令,如下圖:

位元組碼層面了解try、catch、finally

根據異常表我已經圈出了第一個異常處理的指令,從52行開始,astore_2表示将索引存儲到本地變量表中第2項,實際上就是将“FileNotFoundException e”産生的變量e存入,53至56行指令則是将數字2存入數組中,對應代碼“ids[0]=2;”,說明在執行第一個catch中的代碼。

後面的指令可以很明顯的看出來又是在執行finally中的代碼,後面的幾個異常基本相似,都是執行自己的catch中的代碼後再次執行finally中代碼,這裡就不再一一分析了!

總結

根據以上分析可以看出,finally不管是程式正常執行還是有異常,在位元組碼層面都會把finally中的代碼編譯在正常和異常代碼後面,是以有多少個catch,finally代碼就會多編譯多少次,并且return指令都是在finally中的代碼執行完成後才執行!

Java程式員日常學習筆記,如了解有誤歡迎各位交流讨論!

位元組碼層面了解try、catch、finally