java 判断顺序_通过指令码来判断Java代码的执行顺序(++问题与return和finally的问题)...
问题
在《深入理解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的问题)...相关推荐
- java 判断类型_如何快速入门Java编程学习(干货)
一.初识Java 1.生活中的程序: 从起床到教室上课的过程 穿衣打扮>起床>洗漱>出宿舍>>吃早餐>到教室 按照特定的顺序去完成某一件事的过程我们叫做生活中的程序 ...
- java加载顺序_类加载过程中几个重点执行顺序整理
正文前先来一波福利推荐: 福利一: 百万年薪架构师视频,该视频可以学到很多东西,是本人花钱买的VIP课程,学习消化了一年,为了支持一下女朋友公众号也方便大家学习,共享给大家. 福利二: 毕业答辩以及工 ...
- python嵌套循环执行顺序_两个嵌套for循环的执行顺序
展开全部 当两个或2113多个循环语句嵌套时,执行5261顺序按照一下步骤: 1.先判断最外4102层循环条件,若1653满足条件则进入第一层循环体. 2.进入第一层循环体后再次遇到循环语句进行第二层 ...
- java 过滤器执行图_「filterchain」java 过滤器Filter中chain.doFilter()之前和之后代码的执行顺序 - seo实验室...
filterchain 过滤器拦截到请求之后,首先是执行doFilter()方法中chain.doFilter()之前的代码,然后放弃权限给下一个过滤器或者serverlet等等,最后才执行chain ...
- java代码块执行顺序_Java笔记 | Java代码块执行顺序测试
最近笔试常常遇到考察Java代码块执行顺序的题目,网上查看博客错漏百出,特地自己测试了一下. 如有错漏,希望路过的大佬指出来,以便我进行更改. 先上代码吧! public class ClassA { ...
- java子类代码块_java中父类子类静态代码块、构造代码块执行顺序
父类静态(代码块,变量赋值二者按顺序执行) 子类静态 父类构造代码块 父类构造方法 子类构造代码块 子类构造方法 普通方法在实列调用的时候执行,肯定位于上面之后了 //父类A public class ...
- Java代码的执行顺序
代码块的执行顺序如下: 1.静态块 2.父类构造器 3.本类中的块 4.本类的构造器 代码语句执行顺序: 整体是从上到下,从左到右,但是赋值语句,则是从右到左,必须先执行等号右边的语句得到值,再声明变 ...
- 【青少年编程】【答疑】控制Scratch异步代码的执行顺序
问题 几天前,我写了一篇图文 对「等待(0)秒」的理解,发现可以利用「等待(0)秒」这个积木块来解决Scratch中异步代码的执行顺序问题,即点击绿旗后可以控制多个角色中响应该事件的代码的顺序. 在这 ...
- java switch case怎么判断范围_【转】Java期末复习攻略!
期末 19年就这样要过去了, 终于到了小时候作文里的未来呢! 然而,期末考试也随之来临了. 不知大家"预习"的怎么样呢? 期末复习资料的放送快接近尾声了 下面康康学长学姐们 怎么教 ...
最新文章
- 天池供应链大赛来了!
- heroes 2 android,英雄出击2游戏下载-英雄出击2Heroes Strike2中文安卓版下载v0.0.5- 游侠下载站...
- Maven 命令格式及一些常用命令
- Play! Framework 系列(四):DI 模式比较
- hdu4513--Manacher算法--回文串的O(n)算法
- python模拟鼠标拖动滑块_如何通过拖动滑块来控制Kivy滚动视图?
- 【Trie】阅读理解(luogu 3879/ybtoj Trie-4)
- 【树形区间DP】加分二叉树(ssl 1033/luogu 1040)
- python的排序方式
- 【无线也安全】屏蔽蹭网一族
- 菜鸟到高手:SQL开发进阶常用精妙语句
- 安谋中国“星辰”处理器商用:灵动微、全志科技、华大北斗布局合作
- 华为认证HCDA免费公开课课表及其交流群公告
- 桌面版微信打开链接,H5页面一片空白
- 三轴加速度传感器和六轴惯性传感器_六轴传感器和三轴传感器的区别在哪
- Codeforces 235C. Cyclical Quest 后缀自动机
- Oracle数据库表空间不足 ORA-01653:unable to extend table 表名称 by 8192 in tablespace 表空间名称
- 打开SAP物料帐期和财务账期
- 基因测序与高通量测序区别
- 使用OpenCV库函数将图片合成视频
热门文章
- 投篮c语言程序设计,教师招聘笔试体育之篮球必做20题(一)
- 织梦自定义图片字段和缩略图一样_织梦图片集模型自定义图片字段调用
- mysql数据库的三级模式_2016年计算机三级MySQL数据库试题
- 本地提交spark_Spark 数据本地化级别
- Win10桌面的图标都不见了怎么办 Win10电脑桌面图标找回方法
- 钉钉电脑版怎么创建共享文件 钉钉文件共享的方法
- std::tuple还是struct?
- static,构造器,执行顺序
- springboot如何使用log4j记录日志
- IDEA2019版最新配置SVN及上传教程-超详细图文详解