今天无意中,同事发现了一个有趣的现象,首先来看一下以下代码:
java 代码会发现,18行加载了一下变量b中的值,所以,其实,在执行代码的第8行时,并不是执行return,而只是往内存中存储变量b的值。
- public class Test {
- private static int todo() {
- int b=1;
- try{
- int a=2/0;
- } catch(Exception e) {
- System.out.println("in catch");
- return 1;
- } finally {
- System.out.println("in final");
- }
- return 2;
- }
-
-
-
- public static void main(String[] args) {
- System.out.println(todo());
-
- }
- }
此时,运行结果为
in catch
in final
1
看到这里,少部分人已经开始不明白了,我们继续看另一则代码(请注意细小的差别):
java 代码
- public class Test {
- private static int todo() {
- int b=1;
- try{
- int a=2/0;
- } catch(Exception e) {
- System.out.println("in catch");
- return b;
- } finally {
- System.out.println("in final");
- }
- return 2;
- }
-
-
-
- public static void main(String[] args) {
- System.out.println(todo());
-
- }
- }
此时,运行结果依旧是
in catch
in final
1
但是通过单步执行,会发现,两种代码的执行顺序有少许差别
第一种代码的try/catch单步执行顺序为
7->10->8
第二种代码的try/catch单步执行顺序为
7->8->10->8
这里我们对其进行了研究,从中间码入手,首先来分别看看两者的中间码:
上述第一块代码的中间码
- 11 ldc <String "in catch"> [22]
- 13 invokevirtual java.io.PrintStream.println(java.lang.String) : void [24]
- 16 getstatic java.lang.System.out : java.io.PrintStream [16]
- 19 ldc <String "in final"> [30]
- 21 invokevirtual java.io.PrintStream.println(java.lang.String) : void [24]
- 24 iconst_1
- 25 ireturn
可以发现,明显的执行顺序,1行把装入字符串转入内存,2行打印,4行在把字符串装入内存,5行再打印,7行返回
上述第二块代码的中间码
- 13 ldc <String "in catch"> [22]
- 15 invokevirtual java.io.PrintStream.println(java.lang.String) : void [24]
- 18 iload_0 [b]
- 19 istore_3
- 20 getstatic java.lang.System.out : java.io.PrintStream [16]
- 23 ldc <String "in final"> [30]
- 25 invokevirtual java.io.PrintStream.println(java.lang.String) : void [24]
- 28 iload_3
- 29 ireturn
以上的过程,分析下中间码,还是可以理解的,但是非常非常让人奇怪的是,如下的代码(请还是注意不同的细节)
java 代码
- public class Test {
- private static int todo() {
- int b=1;
- try{
- int a=2/0;
- } catch(Exception e) {
- System.out.println("in catch");
- return b;
- } finally {
- b=3;
- System.out.println("in final");
- }
- return 2;
- }
-
-
-
- public static void main(String[] args) {
- System.out.println(todo());
-
- }
- }
此时运行结果照样是
in catch
in final
1
单步执行结果是 7->8->10->11->8
想不通的是,根据单步执行,在finally里对b进行了修改,但是,返回的仍旧是1
再来看看这个代码的中间码:
第三个代码的中间码
- 13 ldc <String "in catch"> [22]
- 15 invokevirtual java.io.PrintStream.println(java.lang.String) : void [24]
- 18 iload_0 [b]
- 19 istore_3
- 20 iconst_3
- 21 istore_0 [b]
- 22 getstatic java.lang.System.out : java.io.PrintStream [16]
- 25 ldc <String "in final"> [30]
- 27 invokevirtual java.io.PrintStream.println(java.lang.String) : void [24]
- 30 iload_3
- 31 ireturn
因为,其实我没怎么学过汇编,只是初步的分析了
3行加载b的地址,4行在地址3储存b的值(此时地址3的值为1),5行加载一个常量,6行把常量存入b的地址(此时b的值为3),10行加载地址3的值,最后返回值。
不知道这样的解释是否合理,也请,希望能够得到一个对于try./catch块执行问题的通俗答案,谢谢!