Java 中的finally你知多少?
可不能小看这个简单的 finally,看似简单的问题背后,却隐藏了无数的玄机。接下来我就带您一步一步的揭开这个 finally 的神秘面纱。
test()); } public static int test() { int i = 1; // if(i == 1) // return 0; System.out.println("the previous statement of try block"); i = i / 0; try { System.out.println("try block"); return i; }finally { System.out.println("finally block"); } } }
?
清单 1 的执行结果如下:
?
另外,如果去掉上例中被注释的两条语句前的注释符,执行结果则是:
test()); } public static int test() { int i = 1; try { System.out.println("try block"); System.exit(0); return i; }finally { System.out.println("finally block"); } } }?
清单 2 的执行结果如下:
test()); } public static int test(){ int i = 1; try { System.out.println("try block"); i = 1 / 0; return 1; }catch (Exception e){ System.out.println("exception block"); return 2; }finally { System.out.println("finally block"); } } }?
清单 4 的执行结果为:
getValue()); } public static int getValue() { try { return 0; } finally { return 1; } } }?
清单 5 的执行结果:
getValue()); } public static int getValue() { int i = 1; try { return i; } finally { i++; } } }?
清单 6 的执行结果:
由上图,我们可以清晰的看出,在 finally 语句块(iinc 0, 1)执行之前,getValue()方法保存了其返回值(1)到本地表量表中 1 的位置,完成这个任务的指令是 istore_1;然后执行 finally 语句块(iinc 0, 1),finally 语句块把位于 0 这个位置的本地变量表中的值加 1,变成 2;待 finally 语句块执行完毕之后,把本地表量表中 1 的位置上值恢复到操作数栈(iload_1),最后执行 ireturn 指令把当前操作数栈中的值(1)返回给其调用者(main)。这就是为什么清单 6 的执行结果是 1,而不是 2 的原因。
再让我们来看看异常执行的情况。是不是有人会问,你的清单 6 中都没有 catch 语句,哪来的异常处理呢?我觉得这是一个好问题,其实,即使没有 catch 语句,Java 编译器编译出的字节码中还是有默认的异常处理的,别忘了,除了需要捕获的异常,还可能有不需捕获的异常(如:RunTimeException 和 Error)。
从 getValue()方法的字节码中,我们可以看到它的异常处理表(exception table), 如下:
Exception table:
from to target type
2 4 9 any
它的意思是说:如果从 2 到 4 这段指令出现异常,则由从 9 开始的指令来处理。
图 2. getValue()函数异常执行的情况
先说明一点,上图中的 exception 其实应该是 exception 对象的引用,为了方便说明,我直接把它写成 exception 了。
由上图(图 2)可知,当从 2 到 4 这段指令出现异常时,将会产生一个 exception 对象,并且把它压入当前操作数栈的栈顶。接下来是 astore_2 这条指令,它负责把 exception 对象保存到本地变量表中 2 的位置,然后执行 finally 语句块,待 finally 语句块执行完毕后,再由 aload_2 这条指令把预先存储的 exception 对象恢复到操作数栈中,最后由 athrow 指令将其返回给该方法的调用者(main)。
通过以上的分析,大家应该已经清楚 try-catch-finally 语句块的执行流程了吧!
为了更具说服力,我们还是来引经据典吧!大家可以不相信我,难道还不相信“高司令”(Gosling)吗?下面这段仍然摘自 Java 语言规范第四版 《The Java? Programming Language, Fourth Edition》,请读者自己体会吧!
*******************************************************************************
a finally clause is always entered with a reason. That reason may be that the try code finished normally, that it executed a control flow statement such as return, or that an exception was thrown in code executed in the Try block. The reason is remembered when the finally clause exits by falling out the bottom. However, if the finally block creates its own reason to leave by executing a control flow statement (such as break or return) or by throwing an exception, that reason supersedes the original one, and the original reason is forgotten. For example, consider the following code:
try {
// … do something …
return 1;
} finally {
return 2;
}
When the Try block executes its return, the finally block is entered with the “reason” of returning the value 1. However, inside the finally block the value 2 is returned, so the initial intention is forgotten. In fact, if any of the other code in the try block had thrown an exception, the result would still be to return 2. If the finally block did not return a value but simply fell out the bottom, the “return the value 1 ″ reason would be remembered and carried out.
*******************************************************************************
好了,有了以上的知识,让我们再来看以下 3 个例子。
清单 7.
?
清单 7 的执行结果:
getValue()); } public static int getValue() { int i = 1; try { i = 4; } finally { i++; } return i; } }?
清单 8 的执行结果:
5 楼 wanggod 2011-11-08 排版吧。。。。。。。。。 6 楼 xxdd328 2011-11-08 如果这是一份应聘人员的简历,如果你是筛选简历的面试官,你看不? 7 楼 albertwxh 2011-11-08 能否排一下版呢?代码看得很头疼! 8 楼 1927105 2011-11-08 walldr2161 写道排下版吧。。。。9 楼 yizhl 2011-11-08 都不排版。。你自己看着不累么? 10 楼 aedmon 2011-11-09 排版。。啊!!!!!!!!!!!!! 11 楼 lydawen 2011-11-09 walldr2161 写道排下版吧。。。。
原文转帖,所以排版都不重要了 12 楼 bingtao115 2011-11-09 很耐心的看了一半,lz总结的很多 13 楼 web118.com 2011-11-09 排版确实恶心。晚上回家整理整理,转到自己的blog上去。 14 楼 wupuyuan 2011-11-09 其实只要搞清楚Java虚拟机的栈的操作原理就很简单,不用讲的这么麻烦,不过总结的还不错。 15 楼 red_xie 2011-11-09 walldr2161 写道排下版吧。。。。
1927105 写道walldr2161 写道排下版吧。。。。
16 楼 grandboy 2011-11-09 文章写得不错,就是排版太差了。尤其是代码块的排版就更差了。 17 楼 feng5199 2011-11-10 感觉在钻牛角尖 18 楼 wangbing9577 2011-11-10 有虚拟机的相关知识会有助与理解
感谢楼主的分享,谢谢! 19 楼 起点NOW 2011-11-10 楼主的"清单1"有些牵强了,碰到"除零"的情况,没有捕获,当然会有提示...
我们说finally一定会执行,是指"对异常进行了相应的处理"这个前提! 20 楼 huangyunbin 2011-11-10 很有收获。。 21 楼 Berson_Cheng 2011-11-21 前几个根本就是为了你所谓的理论而构造出来的,根本就没什么用。尤其是第一个清单,看了我发觉写这个的人是个大菜,看下去越看越蛋疼。还以为有新东西学呢