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语句块中有返回语句,会覆盖抛出的异常,使异常无法抛出

 

posted @ 2020-10-15 11:34  liuyiyuan  阅读(400)  评论(0编辑  收藏  举报