先从一道面试题开始:
如果catch里面有return语句,请问finally的代码还会执行吗?
如果会,请问是在return前还是return后?
例如这段代码,最后主函数打印出的值是多少?是20还是30?
public static void main(String[] args) {
System.out.println(testFinally());
}
public static int testFinally(){
int i=0;
{
try {
System.out.println(10/i);
i=10;
return i;
} catch (Exception e) {
i=20;
return i;
}
finally {
i=30;
System.out.println("finally代码块执行了");
return i;
}
}
答案揭晓:finally语句会被执行,执行时间既不在return前,也不在return后,而是让return先执行一半,最后finally执行完后再执行完return,主函数打印出的值为30。
这个回答可能让人疑惑,什么叫执行一半?catch中不是已经return了i值20吗?为什么会打印出30?
要解释清楚,必须首先知道,在Java运行时jvm(java虚拟机)会给每个调用的方法在内存的栈中产生栈帧,而如果方法会return一个值,那么这个方法所在的栈帧中会单独划分一片区域,用于存储return所要返回的值,例如下图:
代码运行,出现异常,转入catch代码块,i被赋值为20,然后jvm执行return语句,将i的值20存入栈帧上负责存储return值的那块区域
注意,我们知道,finally代码块中的内容一定会被执行,所以jvm将i=20存入return区域后并未返回,而是先去执行finally代码块中的内容,jvm将执行i=30,然后又遇见了return语句,于是再次把新的i值存入return区域,这时不会再有代码打断这条return语句了,于是返回30;
假如finally代码块中没有return语句只有赋值语句呢?例如这样,输出的是20还是30?
public static void main(String[] args) {
System.out.println(testFinally());
}
public static int testFinally(){
int i=0;
{
try {
System.out.println(10/i);
i=10;
} catch (Exception e) {
i=20;
return i;
}
finally {
i=30;
System.out.println("finally");
}
System.out.println("我执行了");
return i;
}
}
运行结果:
finally
20
答案是20,因为finally代码块中没有return也就不会去修改return区域中存储的值,finally代码块执行完毕后直接执行catch中的return,同时结束整个方法的调用,所以后面System.out.println(“我执行了”);这些代码也不会执行