【知識積累】try-catch-finally+return總結

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、總結

  在平時要注意知識點的積累,清楚細小知識點背後的原理。也謝謝各位園友的觀看~

相關文章
相關標籤/搜索