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语句块。

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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/563781.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

java 怎么从性能上优化代码_月薪上万做好这一步:程序员职场中必须掌握的的Java代码性能优化技巧...

尽量指定类、方法的final修饰符Java编译器会寻找机会内联所有的final方法,内联对于提升Java运行效率作用重大,具体参见Java运行期优化。此举大概能够使性能平均提高50%。尽量重用对象特别是String对象的使用,出现字符串连接时应该使用StringB…

java创建线程几种_java中创建线程有几种方式

详细内容线程的创建方式1、继承Thread类实现多线程2、覆写Runnable()接口实现多线程,而后同样覆写run()。推荐此方式3、使用Callable和Future创建线程相关视频教程推荐:java学习视频实例如下:1、继承Thread类实现多线程/** 继承Thread类创建线…

php编译支持mysql,编译php支持curl和pdo_mysql

安装curl1. curl 是 php 标准库,所以可以在原來的 phpX.X.X.tar.gz 中找到。2. 進入 php 目录中的 ext 找到 curl3. 在 curl 目录中执行 phpize #此命令用于生成configure文件(如果没有此软件上网下载安装一个)4. 然后他生成的文件进行 ./configure–with-php-config/usr/local…

php自定义中文分词方法,php实现的中文分词类完整实例

本文实例讲述了php实现的中文分词类。分享给大家供大家参考,具体如下:该中文分词类源码使用http://tools.ddpool.cn/code/jb51_php_format进行了格式化处理,便于阅读。具体代码如下:class Segmentation {var $options array(lowe…

php打开rs232,php_ser_5.2.0-5.2.17

php_ser_5.2.0-5.2.17所属分类:WEB开发开发工具:PHP文件大小:169KB下载次数:62上传日期:2011-09-23 11:30:16上 传 者:随寻说明: php实现RS232串口通信下载php_ser_5.2.0.zip解冻把php_ser放到…

使用php,使用 PHP

使用 PHP推荐查看本文HTML版本本节汇集了你在写 PHP 脚本时可能碰到的大多数普通错误。1. 我想写一个可以处理任何表单来的数据的通用 PHP 脚本。我怎么知道哪个 POST 方法变量可用呢?2. 我需要在所有的单引号()前加一个反斜线(\),使它们变成(\)&#xf…

手机电脑自适应导航源码php,自适应各终端懒人网址导航源码 v2.0

自适应各终端懒人网址导航源码。V2.0版本是在原1.8版本的基础上修复和增加了些功能。推荐直接使用新版本,舍弃旧版本,后期会继续不定期更新。测试环境:宝塔Nginx -Tengine2.2.3的php5.5mysql先导入数据库文件db/db.sql再修改config.php数据库…

php如何生成本地文档,php如何生成word文件

php生成word文件的方法:需求:甲方爸爸说“我有一个word文件,里面是关于用户信息的表格,你帮我把系统里面所有用户数据都按照这个文件导出来给我“;我当时就不乐意了,啪的一拍桌子站起来,愤愤的回…

php 写一个大富翁游戏,C++大富翁代码 现在要设计一个类似“大富翁”的游戏:有一条由20个格子组成的 联合开发网 - pudn.com...

C大富翁代码所属分类:Windows编程开发工具:C/C文件大小:349KB下载次数:6上传日期:2018-05-15 21:11:38上 传 者:红黑二叉树说明: 现在要设计一个类似“大富翁”的游戏:有一条由20个…

yii 引用php文件,Yii中引出php文件及插件

Yii中引入php文件及插件作者:zccst一、设置环境变量(在PHP)echo get_include_path(); //获取当前所有环境变量。即该项目中那些代码已被引用。set_include_path(.. PATH_SEPARATOR . Yii::app()->basePath./lib/PHPExcel . PATH_SEPARATOR . get_include_p…

形态学图像分割matlab算法,图像分割和形态学处理与MATLAB实现

《图像分割和形态学处理与MATLAB实现》由会员分享,可在线阅读,更多相关《图像分割和形态学处理与MATLAB实现(13页珍藏版)》请在人人文库网上搜索。1、实验 图像分割和形态学处理一、实验目的1、 掌握图像分割的基本方法。2、 掌握形态学处理的基本方法。…

php 显示状态,php-fpm status状态配置显示

系统为ubuntu,在usr/etc/sites-enabled/中编辑nginx配置文件server_name demo.com;location ~^/status$ {#auth_basic "status page";#fastcgi_index index.php;fastcgi_param SCRIPT_FILENAME $fastcgi_script_name;include fastcgi_params;fastcgi_pass unix:/var/…

matlab 动态目录调用程序集,C#中如何动态添加程序集查找目录

C#中如何动态添加程序集查找目录情况如下:现有三个程序集Main.exe, One.dll, Two.dll。其中One.dll引用了Two.dll, 并且One.dll与Two.dll部署在一起, 而Main.exe单独部署。在One.dll中有using Two;namespace One{public SomeType{public void DoSomethin…

matlab 红黑强度图片,matlab图形锐化程序

满意答案gveial5852013.10.31采纳率:50% 等级:12已帮助:11498人help imreadhelp fspecial imfilt帮助稳定中有较多的示例fspecial 函数功能:产生预定义滤波器格式:Hfspecial(type)Hfspecial(gaussian,n,sigma) 高斯…

php chr 乱码,php chr() ord()中文截取乱码问题解决方法_PHP教程

复制代码 代码如下:/* 另一种方法&#xff0c;使用ord()函数&#xff1a; 适用于 gb2312 编码:*/$str "怎么将新闻的很长的标题只显示前面一些字&#xff0c;后面用.....来代替&#xff1f;";function gb2312_substr($str, $limit) {$restr ;for($i0;$i< $limit-…

matlab int8 函数,未定义与 'uint8' 类型的输入参数相对应的函数 'fitnessty'

该楼层疑似违规已被系统折叠 隐藏此楼查看此楼程序如下&#xff1a;clear allclcticpopsize15;lanti10;maxgen50;cross_rate0.4;mutation_rate0.1;a00.7;zpopsize5;bestf0;nf0;number0;Iimread(C:\Users\Yu\Pictures\feiji.jpg);if numel(I)>2Irgb2gray(I);end[m,n]size(I);…

php_os用法,golang中os包用法

os包中实现了平台无关的接口&#xff0c;设计向Unix风格&#xff0c;但是错误处理是go风格&#xff0c;当os包使用时&#xff0c;如果失败之后返回错误类型而不是错误数量&#xff0e;os包中函数设计方式和Unix类似&#xff0c;下面来看一下&#xff0e;func Chdir(dir string)…

python n个人围成一圈,Python练习代码实例69-有n个人围成一圈,顺序排号。从第一个人开始报数(从1到3报数),凡报到3的人退出圈子,问最后留下的...

Python 练习实例69题目&#xff1a;有n个人围成一圈&#xff0c;顺序排号。从第一个人开始报数(从1到3报数)&#xff0c;凡报到3的人退出圈子&#xff0c;问最后留下的是原来第几号的那位。程序分析&#xff1a;无。程序源代码&#xff1a;#!/usr/bin/python# -*- coding: UTF-…

php基础学哪些,榆林学习php需要哪些基础(PHP是什么)

榆林学习php需要哪些基础&#xff0c;想多学一门知识&#xff0c;陕西PHP培训对我们都是大有裨益的&#xff0c;紧接着就去了解下php代码运行方式有哪些&#xff0c;php常用框架有哪些&#xff0c;php为啥遭到它行鄙视&#xff0c;PHP是什么&#xff0c;php的性质有哪些&#x…

.deploy 文件 php,关于php:Heroku deploy自动删除服务器文件?

我是HEROKU APPS的新手。在我的heroku应用程序中&#xff0c;我遇到了问题。 那就是我正在使用php脚本将数据保存在服务器上。范例&#xff1a;$file "example.txt";$data "Something...";file_put_contents($file,$data);?>此PHP脚本成功运行并完美保…