java异常捕获分析
思考问题:
调用下面的方法分别会返和输出回什么?
package com.itdragon.controller;
import org.junit.Test;
public class TestMain {
public int test1() {
int a=0;
try {
a=1/0;
} catch (Exception e) {
System.out.println("test1:err");
a=100;
return a;
}finally {
System.out.println("test1:fin");
a=200;
return a;
}
}
public int test2() {
int a=0;
try {
a=1/0;
} catch (Exception e) {
System.out.println("test2:err");
a=100;
return a;
}finally {
System.out.println("test2:fin");
a=200;
}
return a;
}
@Test
public void tests(){
System.out.println("test1:"+test1());
System.out.println();
System.out.println("test2:"+test2());
}
}

众所周知,java捕获到异常时,先执行catch 代码块,如果有finally代码块,那么无论最后有没有异常都会执行finally代码块。
对于test1都好理解,最后在finally中返回,值为200.
但是对于test2就有点难以理解,认为应该返回200,但是却返回了100.
对于上述两个方法简化,在做叙述:
public int test1() {
int a=0;
try {
a=1/0;
} catch (Exception e) {
a=100;
return a;
}finally {
a=200;
return a;
}
}
public int test2() {
int a=0;
try {
a=1/0;
} catch (Exception e) {
a=100;
return a;
}finally {
a=200;
}
return a;
}
反编译出的字节码如下:
test1
public int test1();
descriptor: ()I
flags: ACC_PUBLIC
Code:
stack=2, locals=5, args_size=1
0: iconst_0 //将int型0推送至栈顶
1: istore_1 //将栈顶int型数值存入第二个本地变量
2: iconst_1 //将int型1推送至栈顶
3: iconst_0 //将int型0推送至栈顶
4: idiv //将栈顶两int型数值相除并将结果压入栈顶
5: istore_1 //将栈顶int型数值存入第二个本地变量
6: sipush 200 //将一个短整型常量值(-32768~32767)推送至栈顶
9: istore_1 //将栈顶int型数值存入第二个本地变量
10: iload_1 //将第二个int型本地变量推送至栈顶
11: ireturn //从当前方法返回int
12: astore_2 //将栈顶引用型数值存入第三个本地变量
13: bipush 100 //将单字节的常量值(-128~127)推送至栈顶
15: istore_1 //将栈顶int型数值存入第二个本地变量
16: iload_1 //将第二个int型本地变量推送至栈顶
17: istore_3 //将栈顶int型数值存入第四个本地变量
18: sipush 200 //将一个短整型常量值(-32768~32767)推送至栈顶
21: istore_1 //将栈顶int型数值存入第二个本地变量
22: iload_1 //将第二个int型本地变量推送至栈顶
23: ireturn //从当前方法返回int
24: astore 4 //将栈顶引用型数值存入指定本地变量
26: sipush 200 //将一个短整型常量值(-32768~32767)推送至栈顶
29: istore_1 //将栈顶int型数值存入第二个本地变量
30: iload_1 //将第二个int型本地变量推送至栈顶
31: ireturn //从当前方法返回int
Exception table:
from to target type
2 6 12 Class java/lang/Exception
2 6 24 any
12 18 24 any
24 26 24 any
test2:
public int test1();
descriptor: ()I
flags: ACC_PUBLIC
Code:
stack=2, locals=5, args_size=1
0: iconst_0 //将int型0推送至栈顶
1: istore_1 //将栈顶int型数值存入第二个本地变量
2: iconst_1 //将int型1推送至栈顶
3: iconst_0 //将int型0推送至栈顶
4: idiv //将栈顶两int型数值相除并将结果压入栈顶
5: istore_1 //将栈顶int型数值存入第二个本地变量
6: sipush 200 //将一个短整型常量值(-32768~32767)推送至栈顶
9: istore_1 //将栈顶int型数值存入第二个本地变量
10: goto 34 //无条件跳转
13: astore_2 ////将栈顶引用型数值存入第三个本地变量
14: bipush 100 //将单字节的常量值(-128~127)推送至栈顶
16: istore_1 // 将栈顶int型数值存入第二个本地变量
17: iload_1 //将第二个int型本地变量推送至栈顶
18: istore_3 //将栈顶int型数值存入第四个本地变量
19: sipush 200 // 将一个短整型常量值(-32768~32767)推送至栈顶
22: istore_1 //将栈顶int型数值存入第二个本地变量
23: iload_3 //将第四个int型本地变量推送至栈顶
24: ireturn // 从当前方法返回int
25: astore 4 //将栈顶引用型数值存入指定本地变量
27: sipush 200 //将一个短整型常量值(-32768~32767)推送至栈顶
30: istore_1 //将栈顶int型数值存入第二个本地变量
31: aload 4 //将指定的引用类型本地变量推送至栈顶
33: athrow //将栈顶的异常抛出
34: iload_1 //将第二个int型本地变量推送至栈顶
35: ireturn // 从当前方法返回int
Exception table:
from to target type
2 6 13 Class java/lang/Exception
2 6 25 any
13 19 25 any
25 27 25 any
可能对于不是太了解字节码的同学来说不好理解。
但是从中我的理解是:当有finally块存在的时候,虚拟机执行完catch块中的代码时不会立即返回,而是把return语句给屏蔽了,将catch中的代码执行完以、变量在局部变量表中保存好以后,继续执行finally块中的代码,如果finally块中有返回语句,则直接返回,否则,虚拟机去取在catch块中保存的局部变量,放入栈顶,返回。