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