1. 程式人生 > 實用技巧 >finally語句塊對返回的影響

finally語句塊對返回的影響

Java中用於異常處理的語句方法為:

try {
    //...
} catch (Excaption e){
    //...
} finally {
    //...
} 

其中finally語句塊是一定會執行的,不論是正常返回還是丟擲異常。

那麼就引出一個問題:當在try/catch語句中已經return 返回一個變數,此時再次在finally中操作返回變數或重新return會對原來的返回值造成什麼影響?

1、finally中修改返回變數

    /** 在finally語句塊中修改返回變數的值 */
    static int testFinallyAndReturn1(){
        
int i=10; try{ return i; }finally{ i=i+1; } }
public static void main(String[] args) { int return1 = testFinallyAndReturn1(); System.out.println(return1); //結果為10 }

結果返回依然是return的值,可以說在finally中操作已經return的變數,不會對返回結構有影響。

口說無憑,使用 javap -c

命令反編譯看看:

  static int testFinallyAndReturn1();
    Code:                                   模擬棧和區域性變量表的儲存內容,棧的左側為棧頂,區域性變量表依次索引為0,1,2,...
       0: bipush        10         # 向運算元棧中放入常量10                           棧:10           區域性變量表:
       2: istore_0                 # 將棧頂元素放入區域性變量表的slot 0位置  值為10      棧:             區域性變量表:10
3: iload_0 # 取出區域性變量表slot 0的值入棧 值為10    棧:10 區域性變量表:10 4: istore_1 # 棧頂元素存入slot 1 值為10  棧: 區域性變量表:10 10 5: iload_0 # 區域性變量表slot 0入棧 值為10 棧:10 區域性變量表:10 10 6: iconst_1 # 常量1入棧                 棧:1 10 區域性變量表:10 10 7: iadd # 棧頂2個元素出棧並相加,結果再入棧 值為11 棧:11 區域性變量表:10 10 8: istore_0 # 棧頂元素存入slot 0 值為11      棧: 11 10 9: iload_1 # slot 1入棧      棧:10 11 10 10: ireturn # 棧頂元素返回 11: astore_2 # 下面是出現異常時的指令。。。。 12: iload_0 13: iconst_1 14: iadd 15: istore_0 16: aload_2 17: athrow Exception table: from to target type 3 5 11 any

從上面的位元組碼指令可以看到,雖然return程式碼寫在前面,但是在執行位元組碼時,還是先執行finally的加1操作。

finally做加法操作與return操作的變數,分別被儲存到區域性變量表不同slot中,所以finally中操作變數不會影響返回值。

2、finally中再次return

/** 在finally語句塊中修改返回變數的值,並再次返回變數 */
static int testFinallyAndReturn2(){
    int i=10;
    try{return i;
    }finally{
        i=i+1;
        return i;
    }
}
//----------------------
public static void main(String[] args) {
    int return2 = testFinallyAndReturn2();
    System.out.println(return2); //11

}

結果:finally中返回的值會覆蓋掉之前return的值

位元組碼如下:

  static int testFinallyAndReturn2();
    Code:
       0: bipush        10
       2: istore_0
       3: iload_0
       4: istore_1      # 這裡沒有用到???
       5: iload_0
       6: iconst_1
       7: iadd
       8: istore_0
       9: iload_0       # 不同點在這裡,返回指令前入棧的是slot 0 值為11
      10: ireturn
      11: astore_2
      12: iload_0
      13: iconst_1
      14: iadd
      15: istore_0
      16: iload_0
      17: ireturn
    Exception table:
       from    to  target type
           3     5    11   any

3、finally的返回值會覆蓋掉丟擲的異常

如下,這段程式碼會正常返回11,而不是丟擲異常。

    static int testFinallyAndReturn2(){
        int i=10;
        try{
            int a = i/0;
            return i;
        }finally{
            i=i+1;
            return i;
        }
    }

這段是finally中無返回值時的部分指令:

  15: astore_3
  16: iload_0
  17: iconst_1
  18: iadd
  19: istore_0
  20: aload_3
  21: athrow #丟擲異常


//----------------------------------------------
這個是finally中有返回值時的部分指令:

  15: astore_3
  16: iload_0
  17: iconst_1
  18: iadd
  19: istore_0
  20: iload_0
  21: ireturn #返回

finally中如果有返回值語句,就會用return指令覆蓋掉異常丟擲指令。所以說最好不要在finally中返回結果。

4、簡單總結

  • finally語句塊中僅修改返回變數,不會影響最終的返回結果
  • finally語句塊中有返回語句,會覆蓋之前的返回值
  • finally語句塊中有返回語句,會覆蓋丟擲的異常,使異常無法丟擲