浅析Java try….catch….finally中return语句执行顺序
最近看到一道JAVA笔试题:“try{..}中有return语句,那么紧跟在try后的finally{..}的code会不会执行,什么时候执行?”
finally内的code会执行,但是什么时候执行就难以确定,于是运行一段程序后觉得是在return语句执行后,return语句返回前执行,看代码清楚些
public class Test {
public static void main(String[] args) {
// TODO Auto-generated method stub
System.out.println(new Test().test());
}
public int test(){
int i=1;
try{
System.out.println("try....");
return i+=2;
}
finally{
System.out.println("finally....");
i+=3;
System.out.println(i);
}
}
}
运行结果:
try….
finally….
6
3
根据运行结果可知try{..}先运行,并且return后的expression:i+=2也运行了,此时i=3,再运行finally{…}的代码。此时i=6,但是返回的值却是3;
这就涉及到java方法在栈幀中执行过程,
具体参考http://blog.csdn.net/zq602316498/article/details/38926607,
java方法是在栈帧中执行,栈帧是线程私有栈的单位,执行方法的线程会为每一个方法分配一小块栈空间来作为该方法执行时的内存空间,栈帧分为三个区域:
1. 操作数栈,用来保存正在执行的表达式中的操作数。
2. 局部变量区,用来保存方法中使用的变量,包括方法参数,方法内部声明的变量,以及方法中使用到的对象的成员变量或类的成员变量(静态变量),最后两种变量会复制到局部变量区。
3. 字节码指令区,这个不用解释了,就是方法中的代码翻译成的指令。
return语句
eturn expression是分成两部分执行的:
执行:expression;
执行:return指令;
例如:return x+y;
这句代码先执行x+y,再执行return;首先执行将x以及y从局部变量区复制到操作数栈顶的指令,然后执行加法指令,这个时候结果x+y的值会保存在操作数栈的栈顶,最后执行return指令,返回操作数栈顶的值。
如果方法中有finally语句块,那么return语句又是如何执行的呢?
例如下面这段代码:
try{
return expression;
}finally{
code…..;
}
首先我们知道,finally语句是一定会执行,但他们的执行顺序是怎么样的呢?他们的执行顺序如下:
1、expression,计算该表达式,结果保存在操作数栈顶;
2、操作数栈顶值(expression的结果)复制到局部变量区作为返回值;
3、finally语句块中的代码;
4、将第2步复制到局部变量区的返回值又复制回操作数栈顶;
5、return指令,返回操作数栈顶的值;
我们可以看到,在第一步执行完毕后,整个方法的返回值就已经确定了,由于还要执行finally代码块,因此程序会将返回值暂存在局部变量区,腾出操作数栈用来执行finally语句块中代码,等finally执行完毕,再将暂存的返回值又复制回操作数栈顶。所以无论finally语句块中执行了什么操作,都无法影响返回值,所以试图在finally语句块中修改返回值是徒劳的。因此,finally语句块设计出来的目的只是为了让方法执行一些重要的收尾工作,而不是用来计算返回值的。
所以运行结果返回值为3,而不是6。finally语句在return语句执行后,return语句返回前执行。
如果finally{….}中有return 语句呢?
public class Test {
public static void main(String[] args) {
// TODO Auto-generated method stub
System.out.println(new Test().test());
}
public int test(){
int i=1;
try{
System.out.println("try....");
return i+=2;
}
finally{
System.out.println("finally....");
i+=3;
System.out.println(i);
return i;
}
}
}
运行结果:
try….
finally….
6
6
try中的return 被finally 中的return 覆盖。
如果catch{….}中有return 语句呢
public class Test {
public static void main(String[] args) {
// TODO Auto-generated method stub
System.out.println(new Test().test());
}
public int test(){
int i=1;
try{
System.out.println("try....");
i/=0;
i+=2;
return i;
}catch(Exception e){
System.out.println("catch....");
i+=1;
return i;
}
finally{
System.out.println("finally....");
i+=3;
System.out.println(i);
}
}
运行结果:
try….
catch….
finally….
5
2
先运行try{..},此时i/=0;抛出异常,catch捕捉异常并执行catch{…},执行return语句后,执行完finally{…},return 返回 i 值。并且 i 值不受finally的影响。
如果try….catch….finally中都有return语句呢
public class Test {
public static void main(String[] args) {
// TODO Auto-generated method stub
System.out.println(new Test().test());
}
public int test(){
int i=1;
try{
System.out.println("try....");
i+=2;
return i;
}catch(Exception e){
System.out.println("catch....");
i+=1;
return i;
}
finally{
System.out.println("finally....");
i+=3;
System.out.println(i);
return i;
}
}
}
运行结果
try….
finally….
6
6
public class Test {
public static void main(String[] args) {
// TODO Auto-generated method stub
System.out.println(new Test().test());
}
public int test(){
int i=1;
try{
System.out.println("try....");
i/=0;
i+=2;
return i;
}catch(Exception e){
System.out.println("catch....");
i+=1;
return i;
}
finally{
System.out.println("finally....");
i+=3;
System.out.println(i);
return i;
}
}
}
运行结果
try….
catch….
finally….
5
5
可以看出,不管有无异常,finally的return语句都会进行覆盖。
最后总结:finally块的语句在try或catch中的return语句执行之后返回之前执行,且finally里的修改语句可能影响也可能不影响try或catch中return已经确定的返回值,若finally里也有return语句则覆盖try或catch中的return语句直接返回。