1、前言html
對於找Java相關工做的讀者而言,在筆試中確定免不了遇到try-catch-finally + return的題型,須要面試這清楚返回值,這也是這篇博文產生的由來。本文將從字節碼層面來解釋爲什麼不一樣的寫法對應的返回結果不相同。固然在實際開發環境下不會這麼摳知識點,可是掌握這種分析方法也何嘗不可。對於沒有此需求的讀者大可略過此篇博文。java
2、測試面試
首先進行一個小小的測試,有以下代碼 測試
package com.hust.grid.leesf.main; /** * Created by LEESF on 2016/9/11. */ public class FinallyReturnTest { public static void main(String[] args) { System.out.println(test1()); System.out.println(test2()); System.out.println(test3()); System.out.println(test4()); System.out.println(test5()); System.out.println(test6()); } /** * 有異常拋出狀況、try、catch、finally均有return語句 * @return */ public static int test1() { int x = 0; try { int i = 0; int j = 1; int k = j / i; return x; } catch (Exception e) { x = 1; return x; } finally { x = 2; return x; } } /** * 無異常拋出狀況、try、catch、finally均有return語句 * @return */ public static int test2() { int x = 0; try { int i = 0; int j = 1; return x; } catch (Exception e) { x = 1; return x; } finally { x = 2; return x; } } /** * 有異常拋出,try、catch均有return語句 * @return */ public static int test3() { int x = 0; try { int i = 0; int j = 1; int k = j / i; return x; } catch (Exception e) { x = 1; return x; } finally { x = 2; } } /** * 無異常拋出,try、catch均有return語句 * @return */ public static int test4() { int x = 0; try { int i = 0; int j = 1; return x; } catch (Exception e) { x = 1; return x; } finally { x = 2; } } /** * 無異常拋出,try、方法最後均有return語句 * @return */ public static int test5() { int x = 0; try { int i = 0; int j = 1; return x; } catch (Exception e) { x = 1; } finally { x = 2; } return x; } /** * 有異常拋出,try、方法最後均有return語句 * @return */ public static int test6() { int x = 0; try { int i = 0; int j = 1; int k = j / i; return x; } catch (Exception e) { x = 1; } finally { x = 2; } return x; } }
有興趣的讀者能夠自行測試一下,看可否很完整無誤的寫出正確答案。以上程序的運行結果以下this
2 2 1 0 0 2
3、分析spa
針對上面的結果進行以下的分析。code
使用javap -c FinallyReturnTest.class命令查看FinallyReturnTest的字節碼,具體以下。 htm
Compiled from "FinallyReturnTest.java" public class com.hust.grid.leesf.main.FinallyReturnTest { public com.hust.grid.leesf.main.FinallyReturnTest(); Code: 0: aload_0 1: invokespecial #1 // Method java/lang/Object."<init>": ()V 4: return public static void main(java.lang.String[]); Code: 0: getstatic #2 // Field java/lang/System.out:Ljava/ io/PrintStream; 3: invokestatic #3 // Method test1:()I 6: invokevirtual #4 // Method java/io/PrintStream.printl n:(I)V 9: getstatic #2 // Field java/lang/System.out:Ljava/ io/PrintStream; 12: invokestatic #5 // Method test2:()I 15: invokevirtual #4 // Method java/io/PrintStream.printl n:(I)V 18: getstatic #2 // Field java/lang/System.out:Ljava/ io/PrintStream; 21: invokestatic #6 // Method test3:()I 24: invokevirtual #4 // Method java/io/PrintStream.printl n:(I)V 27: getstatic #2 // Field java/lang/System.out:Ljava/ io/PrintStream; 30: invokestatic #7 // Method test4:()I 33: invokevirtual #4 // Method java/io/PrintStream.printl n:(I)V 36: getstatic #2 // Field java/lang/System.out:Ljava/ io/PrintStream; 39: invokestatic #8 // Method test5:()I 42: invokevirtual #4 // Method java/io/PrintStream.printl n:(I)V 45: getstatic #2 // Field java/lang/System.out:Ljava/ io/PrintStream; 48: invokestatic #9 // Method test6:()I 51: invokevirtual #4 // Method java/io/PrintStream.printl n:(I)V 54: return public static int test1(); Code: 0: iconst_0 1: istore_0 2: iconst_0 3: istore_1 4: iconst_1 5: istore_2 6: iload_2 7: iload_1 8: idiv 9: istore_3 10: iload_0 11: istore 4 13: iconst_2 14: istore_0 15: iload_0 16: ireturn 17: astore_1 18: iconst_1 19: istore_0 20: iload_0 21: istore_2 22: iconst_2 23: istore_0 24: iload_0 25: ireturn 26: astore 5 28: iconst_2 29: istore_0 30: iload_0 31: ireturn Exception table: from to target type 2 13 17 Class java/lang/Exception 2 13 26 any 17 22 26 any 26 28 26 any public static int test2(); Code: 0: iconst_0 1: istore_0 2: iconst_0 3: istore_1 4: iconst_1 5: istore_2 6: iload_0 7: istore_3 8: iconst_2 9: istore_0 10: iload_0 11: ireturn 12: astore_1 13: iconst_1 14: istore_0 15: iload_0 16: istore_2 17: iconst_2 18: istore_0 19: iload_0 20: ireturn 21: astore 4 23: iconst_2 24: istore_0 25: iload_0 26: ireturn Exception table: from to target type 2 8 12 Class java/lang/Exception 2 8 21 any 12 17 21 any 21 23 21 any public static int test3(); Code: 0: iconst_0 1: istore_0 2: iconst_0 3: istore_1 4: iconst_1 5: istore_2 6: iload_2 7: iload_1 8: idiv 9: istore_3 10: iload_0 11: istore 4 13: iconst_2 14: istore_0 15: iload 4 17: ireturn 18: astore_1 19: iconst_1 20: istore_0 21: iload_0 22: istore_2 23: iconst_2 24: istore_0 25: iload_2 26: ireturn 27: astore 5 29: iconst_2 30: istore_0 31: aload 5 33: athrow Exception table: from to target type 2 13 18 Class java/lang/Exception 2 13 27 any 18 23 27 any 27 29 27 any public static int test4(); Code: 0: iconst_0 1: istore_0 2: iconst_0 3: istore_1 4: iconst_1 5: istore_2 6: iload_0 7: istore_3 8: iconst_2 9: istore_0 10: iload_3 11: ireturn 12: astore_1 13: iconst_1 14: istore_0 15: iload_0 16: istore_2 17: iconst_2 18: istore_0 19: iload_2 20: ireturn 21: astore 4 23: iconst_2 24: istore_0 25: aload 4 27: athrow Exception table: from to target type 2 8 12 Class java/lang/Exception 2 8 21 any 12 17 21 any 21 23 21 any public static int test5(); Code: 0: iconst_0 1: istore_0 2: iconst_0 3: istore_1 4: iconst_1 5: istore_2 6: iload_0 7: istore_3 8: iconst_2 9: istore_0 10: iload_3 11: ireturn 12: astore_1 13: iconst_1 14: istore_0 15: iconst_2 16: istore_0 17: goto 27 20: astore 4 22: iconst_2 23: istore_0 24: aload 4 26: athrow 27: iload_0 28: ireturn Exception table: from to target type 2 8 12 Class java/lang/Exception 2 8 20 any 12 15 20 any 20 22 20 any public static int test6(); Code: 0: iconst_0 1: istore_0 2: iconst_0 3: istore_1 4: iconst_1 5: istore_2 6: iload_2 7: iload_1 8: idiv 9: istore_3 10: iload_0 11: istore 4 13: iconst_2 14: istore_0 15: iload 4 17: ireturn 18: astore_1 19: iconst_1 20: istore_0 21: iconst_2 22: istore_0 23: goto 33 26: astore 5 28: iconst_2 29: istore_0 30: aload 5 32: athrow 33: iload_0 34: ireturn Exception table: from to target type 2 13 18 Class java/lang/Exception 2 13 26 any 18 21 26 any 26 28 26 any }
針對上面的字節碼,筆者並不打算從頭開始分析,咱們着重關心的幾個test方法,有興趣的讀者能夠參考博主以前寫過JVM相關的文章來理解其餘內容。傳送門:點這裏blog
3.1 test1方法分析 ci
public static int test1(); Code: 0: iconst_0 // 常量0 1: istore_0 // 將常量0存在局部變量表的第一個slot,值得注意的是該方法爲靜態方法,因此局部變量表的第一個slot並不是存儲的this引用 2: iconst_0 // 常量0 3: istore_1 // 將常量0存在局部變量表的第二個slot 4: iconst_1 // 常量1 5: istore_2 // 將常量1存在局部變量表的第三個slot 6: iload_2 // 將局部變量表的第三個slot的值(1)放到棧頂 7: iload_1 // 將局部變量表的第二個slot的值(0)放到棧頂 8: idiv // 棧頂元素與次棧頂元素相除,次棧頂元素爲除數 9: istore_3 // 將相除的結果保存在局部變量表的第四個slot 10: iload_0 // 將局部變量表的第一個slot的值(0)放到棧頂 11: istore 4 // 將棧頂元素放入局部變量表的第五個slot 13: iconst_2 // 常量2 14: istore_0 // 將常量2存在局部變量表的第一個slot 15: iload_0 // 將局部變量表的第一個slot的值(2)放到棧頂 16: ireturn // 將棧頂的元素返回(返回2) 17: astore_1 // 給catch中定義的Exception e賦值,存在第二個slot中 18: iconst_1 // 常量1 19: istore_0 // 將常量1存在局部變量表的第一個slot 20: iload_0 // 將局部變量表的第一個slot的值(1)放到棧頂 21: istore_2 // 將棧頂元素(1)存在局部變量表的第三個slot中 22: iconst_2 // 常量2 23: istore_0 // 將常量2保存在局部變量表的第一個slot中 24: iload_0 // 將局部變量表的第一個slot的值(2)放到棧頂 25: ireturn // 將棧頂的元素返回(返回2) 26: astore 5 // 給非Exception類型的異常賦值,存在局部變量表的第六個slot中 28: iconst_2 // 常量2 29: istore_0 // 將常量1存在局部變量表的第一個slot 30: iload_0 // 將局部變量表的第一個slot的值(2)放到棧頂 31: ireturn // 將站定的元素返回(返回2) Exception table: // 異常表 from to target type 2 13 17 Class java/lang/Exception // 當2-13行(對應try語句塊)發生屬於Exception異常時,則轉到17行處理(對應catch語句塊) 2 13 26 any // 當2-13行(對應try語句塊)發生屬於非Exception異常時,則轉到26行處理(對應finally語句塊) 17 22 26 any // 當17-22行(對應catch語句塊)發生異常時,則轉到26行處理(對應finally語句塊) 26 28 26 any // 當26-28行(對應finally語句塊)發生異常時,則轉到26行處理(對應finally語句塊)
如上圖所示,筆者對test1方法的每一條指令進行了詳細的說明,能夠看到,不管發生異常與否,最後返回的都是2(16行、25行、31行)。對於test2 -> test6方法,都可以依葫蘆畫瓢進行分析,而後獲得返回值。
4、分析總結
不管發生異常與否,finally語句塊必定在return以前執行。
如下結論基於以下前提:try的return語句在發生異常語句以後、方法有返回值。
① 當finally語句塊中存在return語句時,此時不管是否發生異常,都以該return語句爲準,其餘的return語句將不起做用。
② 當發生異常時,try存在return語句,catch存在return語句,以catch中的return語句爲準,此時在finally語句塊中的修改將不起做用。
③ 當發生異常時,try存在return語句,catch不存在return語句,方法在最後存在return語句,以該return語句爲準,此時在catch和finally的修改會起做用。
④ 不發生異常時,try存在return語句,無論是catch存在return語句仍是方法最後存在return語句,都以try中的return語句爲準,此時在catch、finally中的修改將不起做用。
5、總結
在平時要注意知識點的積累,清楚細小知識點背後的原理。也謝謝各位園友的觀看~