我在上一篇中总结了java中finally结构的3个典型问题,以及展示了finally结构可能产生的副作用。在这一篇中,我将继续展示finally的一些特点。
如果try块中有导致try块提前退出的语句(如return语句和throw语句,我们以return语句为例),那么finally会在return语句之前先执行。
比如有如下函数:
public static int testVariableChangeInFinally()
{
int i=0;
try{
i=1;//步骤1
return i;//步骤2
}finally{
i=2;//步骤3
}
}
那么,函数真正的执行顺序应该是
步骤1->步骤3->步骤2。然而执行该函数时,函数的返回值却是1。why?这导致我们有2个新的假设:
1,执行顺序不是我们想的那样;
2,步骤3根本就没执行;
第2个假设是很好排除的,我们在步骤3下面再加入一条输出语句,形成代码如下
/**
*
* @return
*/
public static int testVariableChangeInFinally()
{
int i=0;
try{
i=1;
return i;
}finally{
i=2;
System.out.println("finally");
}
}
运行函数,发现控制台确实打印出了finally字符串,说明步骤3确实是被执行过了。说明假设2不成立。(如果是怀疑是乱排序了,那么可以用eclipse一步一步调试)
难道执行顺序不是步骤1->步骤3->步骤2吗?答案是只对了1半。
实际上,在虚拟机内部,return语句是分为2步的,第一步是把返回值压入到调用者的栈中,第二步是返回到调用者的栈。因此,我们可以把我们的代码想象成如下形式:
/**
*
* @return
*/
public static int testVariableChangeInFinally() {
int i = 0;
try {
i = 1;//步骤1
int temp = i;//步骤2
return temp;//步骤3
} finally {
i = 2;//步骤4
}
}
因此,执行顺序应该是
步骤1->步骤2->步骤4->步骤3。
所以,函数也就返回了1,而不是2。我们也可以通过eclipse的单步调试观察i值的变化,发现i也确实变成了2。