问题

在《深入理解Java虚拟机》一书中遇到了如下代码:

public int method() {

int i;

try {

i = 1;

return i;

} catch (Exception e) {

i = 2;

return i;

} finally {

i = 3;

}

}

由于曾经搜了一下return和finally的问题后,只是简单的看到了finally会执行,从而导致自己误以为只是简单地把finally的执行顺序放到return语句之前,因此判断这段代码的执行结果应该是3,可实际运行结果是1。研究后发现自己当初真是太糊涂,于是便记录下来。

工具

我们都知道,class文件中的内容就是可供JVM理解的字节码,JVM也是根据class的字节码来执行程序代码,所以class文件中就包含着程序代码最终的执行顺序。

我们可以通过官方提供的javap -c 再加上class文件的路径来得到各个方法对应的指令码。

例如:javap -c Test.class

引例

由于是打算使用JVM的指令码来解决这个问题,刚开始先以一个简单的方法来说明一下。对于如下方法:

public int method1() {

int i = 1;

return i;

}

该方法对应的指令码为:

public int method1();

Code:

0: iconst_1

1: istore_1

2: iload_1

3: ireturn

每个指令对应着一个操作,上面的指令码意思是:

将int型数值0推送至栈顶

将栈顶int型元素存入第二个空间中

将第二个空间的int型元素推送至栈顶

返回将栈顶的int型元素并退出这个方法

由此可以看出,通过指令码,我们可以直观地看到程序代码的执行顺序,这对于解决任何执行顺序的问题是一个利器。

如果还是感觉有些不明所以,那我们可以再看看i++和++i的问题。对于如下代码:

// return 1

public int method2() {

int i = 1;

return i++;

}

// return 2

public int method3() {

int i = 1;

return ++i;

}

它们的指令码分别是:

public int method2();

Code:

0: iconst_1

1: istore_1

2: iload_1

3: iinc 1, 1

6: ireturn

public int method3();

Code:

0: iconst_1

1: istore_1

2: iinc 1, 1

5: iload_1

6: ireturn

显然,这两段指令码最大的区别就是iinc 1,1指令的位置不同,而且如果把这条指令删除,那么与method1的指令码完全一致,对应源代码来看,这条指令就是++这个符号的影响了。

而这个关键的iinc 1,1指令的作用哪怕完全不懂也能猜出来,就是将第二个空间的int数据+1后再放回第二个空间。

将这个含义放到指令码中再重新捋一遍,以method2为例:

将int型数值0推送至栈顶

将栈顶int型元素存入第二个空间中

将第二个空间的int型元素(1)推送至栈顶

将第二个空间的int数据+1后再放回第二个空间

返回将栈顶的int型元素并退出这个方法

需要注意的是,第三步是将1而不是整个空间推送至栈顶,所以第四步对第二个空间中的数据1加1后并没有改变栈顶的值,因此返回值为1。相对的,method2则是:

将int型数值0推送至栈顶

将栈顶int型元素存入第二个空间中

将第二个空间的int数据+1后再放回第二个空间

将第二个空间的int型元素(2)推送至栈顶

返回将栈顶的int型元素并退出这个方法

所以,返回的是2。

解决

现在我们可以看最初的method方法了,在这里再复制一遍代码:

public int method() {

int i;

try {

i = 1;

return i;

} catch (Exception e) {

i = 2;

return i;

} finally {

i = 3;

}

}

对应的指令码:

public int method();

Code:

0: iconst_1

1: istore_1

2: iload_1

3: istore 4

5: iconst_3

6: istore_1

7: iload 4

9: ireturn

10: astore_2

11: iconst_2

12: istore_1

13: iload_1

14: istore 4

16: iconst_3

17: istore_1

18: iload 4

20: ireturn

21: astore_3

22: iconst_3

23: istore_1

24: aload_3

25: athrow

Exception table:

from to target type

0 5 10 Class java/lang/Exception

0 5 21 any

10 16 21 any

这段指令码不同的地方在于最后有一个异常表,我们先不用管它,先看到第一个ireturn指令的指令码,即代码中的第9行为止的指令码:

0: iconst_1

1: istore_1

2: iload_1

3: istore 4

5: iconst_3

6: istore_1

7: iload 4

9: ireturn

这段指令码就是当没有异常时,程序执行的指令码,finally语句块的指令码已经包含在里面了:

将int型数值1推送至栈顶

将栈顶int型元素存入第二个空间中

将第二个空间的int型元素(1)推送至栈顶

将栈顶int型元素存入第五个空间中

将int型数值3推送至栈顶

将栈顶int型元素存入第二个空间中(3)

将第五个空间的int型元素(1)推送至栈顶

返回将栈顶的int型元素并退出这个方法

由此可以看出,方法返回的是第五个空间的1而不是第二个空间的3,和运行结果一致。

其中,关键的地方就是第四步以及第七步。由此可见,Java程序在执行时遇到return语句时,会先将方法的返回值保存起来,如果还有finally语句块,那么就先执行finally语句块,最后再将返回值取出后返回。

另外,如果return后跟的是表达式或者方法,那么会先计算出最终的返回值后再执行finally语句块,可自行验证。

当然,如果保存的返回值是一个引用类型的变量,那么在finally代码块中修改则会改变这个变量本身的属性,因而改变返回值的属性,毕竟finally的代码是的的确确执行过了。

例如,返回一个List,在finally中又对List进行了增加或删除,那么返回的List的内容自然也变了。

附加

关于指令码其余的部分,涉及到更多知识,在这里根据我的理解简单说一下。

这段指令码最后有一个异常表,它的含义可以简单解释为:在[from,to)的区间内,如果发生type类型的异常,那么就跳到target执行。

正因为有了异常表的存在,在出现异常时,程序可以根据产生的异常来跳到正确的位置执行接下来的代码。

[10,20]即为catch代码块对应的指令码,不过其中会把捕捉到的异常存储下来,也就是源代码中的Exception e。[21,25]则是会把try语句块中抛出的catch没有捕捉的异常保存下来,然后执行finally的代码,最后抛出该异常结束方法。

这三片指令码都包含了finally的指令码,也就保证了源代码中finally的代码肯定会执行。

结论

Java程序在执行时遇到return语句时,会先将方法的返回值保存起来,如果还有finally语句块,那么就先执行finally语句块,最后再将返回值取出后返回。另外,如果return后跟的是表达式或者方法,那么会先计算出最终的返回值后再执行finally语句块。

笔记内容只是本人思考而写,如果有什么问题,还请指出,谢谢!

java 判断顺序_通过指令码来判断Java代码的执行顺序(++问题与return和finally的问题)...相关推荐

  1. java 判断类型_如何快速入门Java编程学习(干货)

    一.初识Java 1.生活中的程序: 从起床到教室上课的过程 穿衣打扮>起床>洗漱>出宿舍>>吃早餐>到教室 按照特定的顺序去完成某一件事的过程我们叫做生活中的程序 ...

  2. java加载顺序_类加载过程中几个重点执行顺序整理

    正文前先来一波福利推荐: 福利一: 百万年薪架构师视频,该视频可以学到很多东西,是本人花钱买的VIP课程,学习消化了一年,为了支持一下女朋友公众号也方便大家学习,共享给大家. 福利二: 毕业答辩以及工 ...

  3. python嵌套循环执行顺序_两个嵌套for循环的执行顺序

    展开全部 当两个或2113多个循环语句嵌套时,执行5261顺序按照一下步骤: 1.先判断最外4102层循环条件,若1653满足条件则进入第一层循环体. 2.进入第一层循环体后再次遇到循环语句进行第二层 ...

  4. java 过滤器执行图_「filterchain」java 过滤器Filter中chain.doFilter()之前和之后代码的执行顺序 - seo实验室...

    filterchain 过滤器拦截到请求之后,首先是执行doFilter()方法中chain.doFilter()之前的代码,然后放弃权限给下一个过滤器或者serverlet等等,最后才执行chain ...

  5. java代码块执行顺序_Java笔记 | Java代码块执行顺序测试

    最近笔试常常遇到考察Java代码块执行顺序的题目,网上查看博客错漏百出,特地自己测试了一下. 如有错漏,希望路过的大佬指出来,以便我进行更改. 先上代码吧! public class ClassA { ...

  6. java子类代码块_java中父类子类静态代码块、构造代码块执行顺序

    父类静态(代码块,变量赋值二者按顺序执行) 子类静态 父类构造代码块 父类构造方法 子类构造代码块 子类构造方法 普通方法在实列调用的时候执行,肯定位于上面之后了 //父类A public class ...

  7. Java代码的执行顺序

    代码块的执行顺序如下: 1.静态块 2.父类构造器 3.本类中的块 4.本类的构造器 代码语句执行顺序: 整体是从上到下,从左到右,但是赋值语句,则是从右到左,必须先执行等号右边的语句得到值,再声明变 ...

  8. 【青少年编程】【答疑】控制Scratch异步代码的执行顺序

    问题 几天前,我写了一篇图文 对「等待(0)秒」的理解,发现可以利用「等待(0)秒」这个积木块来解决Scratch中异步代码的执行顺序问题,即点击绿旗后可以控制多个角色中响应该事件的代码的顺序. 在这 ...

  9. java switch case怎么判断范围_【转】Java期末复习攻略!

    期末 19年就这样要过去了, 终于到了小时候作文里的未来呢! 然而,期末考试也随之来临了. 不知大家"预习"的怎么样呢? 期末复习资料的放送快接近尾声了 下面康康学长学姐们 怎么教 ...

最新文章

  1. 天池供应链大赛来了!
  2. heroes 2 android,英雄出击2游戏下载-英雄出击2Heroes Strike2中文安卓版下载v0.0.5- 游侠下载站...
  3. Maven 命令格式及一些常用命令
  4. Play! Framework 系列(四):DI 模式比较
  5. hdu4513--Manacher算法--回文串的O(n)算法
  6. python模拟鼠标拖动滑块_如何通过拖动滑块来控制Kivy滚动视图?
  7. 【Trie】阅读理解(luogu 3879/ybtoj Trie-4)
  8. 【树形区间DP】加分二叉树(ssl 1033/luogu 1040)
  9. python的排序方式
  10. 【无线也安全】屏蔽蹭网一族
  11. 菜鸟到高手:SQL开发进阶常用精妙语句
  12. 安谋中国“星辰”处理器商用:灵动微、全志科技、华大北斗布局合作
  13. 华为认证HCDA免费公开课课表及其交流群公告
  14. 桌面版微信打开链接,H5页面一片空白
  15. 三轴加速度传感器和六轴惯性传感器_六轴传感器和三轴传感器的区别在哪
  16. Codeforces 235C. Cyclical Quest 后缀自动机
  17. Oracle数据库表空间不足 ORA-01653:unable to extend table 表名称 by 8192 in tablespace 表空间名称
  18. 打开SAP物料帐期和财务账期
  19. 基因测序与高通量测序区别
  20. 使用OpenCV库函数将图片合成视频

热门文章

  1. 投篮c语言程序设计,教师招聘笔试体育之篮球必做20题(一)
  2. 织梦自定义图片字段和缩略图一样_织梦图片集模型自定义图片字段调用
  3. mysql数据库的三级模式_2016年计算机三级MySQL数据库试题
  4. 本地提交spark_Spark 数据本地化级别
  5. Win10桌面的图标都不见了怎么办 Win10电脑桌面图标找回方法
  6. 钉钉电脑版怎么创建共享文件 钉钉文件共享的方法
  7. std::tuple还是struct?
  8. static,构造器,执行顺序
  9. springboot如何使用log4j记录日志
  10. IDEA2019版最新配置SVN及上传教程-超详细图文详解