try catch finally执行流程原理分析
try catch finally语句执行流程原理分析
碰到一道讨人try catch语句的面试题,代码如下:
package test;
public class test {
public static void main(String[] args) {
// TODO Auto-generated method stub
System.out.println(test.testTryCatch());
}
public static int testTryCatch() {
int x = 1;
try {
return x;
}
finally {
++ x;
}
}
}
这道题的输出是1,最开始碰到的时候,我以为答案应该是2,因为finally语句块肯定会执行啊!执行了的话那为啥值还不增加呢?后来查了一些博客,发现一篇用虚拟机的字节码指令来解释try catch执行流程的博客,但是觉得解释的不是很清楚(这里就不列出来了啊!懒得去翻了 = =),然后自己也去实验了一下,用jdk 自带的javap程序查看上述代码的class文件(注意加上参数-c),可得到下面的指令序列(这里就列出testTryCatch方法的指令序列了)
上面指令是可在Wiki百科上查阅具体作用,这里就不累赘了。
下面说一下为什么会是输出1。
首先,第零条和第一条指令对应函数中的那条x=1语句,效果就是先把int型的1常量push入操作数栈里,然后又pop出放入局部变量表中的第零个slot(在深入理解java虚拟机书中说第零个slot默认应该是用来放该方法所属对象实例的引用,但是我们这里的testTryCatch是声明为了静态方法,所以就把第一个局部变量放入了第零个slot)。
第二条,第三条,第七条和第八条对应try块里那条return语句,这里说明return语句执行有两步:第一步是把return后面的要返回的值计算出并放入局部变量表特定的位置(一般是放到要用到的变量之后的一个slot),第二步就是把之前局部变量表中要返回的值推入栈顶,并执行ireturn指令,将栈顶的值出栈并返回。第一步和第二步之间就是执行的finally语句快中的代码,如第四条指令iinc,这条指令对应的是++x这条语句,就是把x所在的第零个slot中的值自增1。
ireturn指令之后的第九条到第十四条指令是出现异常后的执行路径,这里就简单说一下:大体就是,把生成的异常对象的引用放入第一个slot,然后再执行finally块里的代码,然后使用athrow抛出保存的那个引用,实际上跟正常执行的流程是差不多的
这里try catch finally语句算是讲清楚了,总的来说就是总共try catch finally语句有一条正常执行路径,一条未声明异常的处理路径,加上已经显示声明的catch语句数量的路径,就像异常表里描述的那样。然后每条路径的Treturn 或 athrow都是有两步,分别为保存值和返回值,两部中间是执行finally代码块。