Java进阶--Java中 i++完全解析
来源:互联网 发布:成都大数据产业研究院 编辑:程序博客网 时间:2024/05/21 19:45
在讲 i++与++i之前先看两个在笔试面试中经常遇到的题目:
题目1
//代码1 int x=2; int b=(x++)*3; System.out.println(b);/* 输出结果为:6*/
题目2
//代码1 int i=0; for(int j=0;j<100;j++) i=i++; System.out.println(i);/* 输出结果为:0*/
题目3
public int inc() { int x; try { x=1; return x; } catch (Exception e) { x=2; return x; } finally{ x=3; } }
上面的输出结果在Java中是正确的,跟Java自身的处理机制有关,Java虚拟机执行字节码是基于栈的体系结
构。 下面就来具体分析一下为什么会有如此奇怪的结果,在Java中变量在运算的时候涉及到两个区域这两个区域分别为 stack 区域和 local variable 区域,stack 在变量进行运算时会用到,而local variable区域用来保存局部变量的值。先看看题目1的字节码:
L0 LINENUMBER 7 L0 ICONST_2 ISTORE 1 L1 LINENUMBER 8 L1 ILOAD 1 IINC 1 1 ICONST_3 IMUL ISTORE 2 L2 LINENUMBER 9 L2 GETSTATIC java/lang/System.out : Ljava/io/PrintStream; ILOAD 2 INVOKEVIRTUAL java/io/PrintStream.println (I)V L3 LINENUMBER 14 L3 RETURN
先对上面的指令进行一个讲解:
① ICONST_2:将2压入到stack中。
②ISTORE 1:将stack中的值取出存放到 local variable 区域的位置1处。
③ILOAD 1:local variable 区域的位置1处的值压入到stack中。
④IINC 1 1:将local variable 区域的位置1处的值加1。
⑤ICONST_3:将3压入到stack中。
⑥IMUL:弹出stack中的两个元素,相乘将结果压入到stack中。
⑦ISTORE 2:将stack中的结果取出存放到local variable区域。
下面是题目1运行时stack和local variable区域的值的情况:
① ICONST_2 :将2压入到stack中。
②ISTORE 1:将stack中的值取出存放到 local variable 区域的位置1处。
③ILOAD 1:local variable 区域的位置1处的值压入到stack中。
④IINC 1 1:将local variable 区域的位置1处的值加1。
⑤ICONST_3:将3压入到stack中。
⑥IMUL:弹出stack中的两个元素,相乘将结果压入到stack中。
⑦ISTORE 2:将stack中的结果取出存放到local variable区域。
最后输出的是local variable中位置2的值,上面计算结果比较好理解关键在于④IINC 1 1这一步直接将local variable中位置1的值加1并不是放到stack中加1,stack中的2后面一直未改变。也就是说stack相当于一个中转站。
题目1清楚后题目2也是比较好理解的,下面直接上题目2的字节码,对照着字节码看更清楚整个流程。
L0 LINENUMBER 7 L0 ICONST_0 ISTORE 1 L1 LINENUMBER 8 L1 ICONST_0 ISTORE 2 L2 GOTO L3 L4 LINENUMBER 9 L4 FRAME APPEND [I I] ILOAD 1 IINC 1 1 ISTORE 1 L5 LINENUMBER 8 L5 IINC 2 1 L3 FRAME SAME ILOAD 2 BIPUSH 100 IF_ICMPLT L4 L6 LINENUMBER 10 L6 GETSTATIC java/lang/System.out : Ljava/io/PrintStream; ILOAD 1 INVOKEVIRTUAL java/io/PrintStream.println (I)V L7 LINENUMBER 11 L7 RETURN//-----------------------------------字节码public int inc();Code:Stack=1,Locals=5,Args_size=10:iconst_1//try块中的x=11:istore_12:iload_1//保存x到returnValue中,此时x=13:istore 45:iconst_3//finaly块中的x=36:istore_17:iload 4//将returnValue中的值放到栈顶,准备给ireturn返回9:ireturn10:astore_2//给catch中定义的Exception e赋值,存储在Slot 2中11:iconst_2//catch块中的x=212:istore_113:iload_1//保存x到returnValue中,此时x=214:istore 416:iconst_3//finaly块中的x=317:istore_118:iload 4//将returnValue中的值放到栈顶,准备给ireturn返回20:ireturn21:astore_3//如果出现了不属于java.lang.Exception及其子类的异常才会走到这里22:iconst_3//finaly块中的x=323:istore_124:aload_3//将异常放置到栈顶,并抛出25:athrowException table:from to target type0 5 10 Class java/lang/Exception0 5 21 any10 16 21 any
题目3中涉及到异常,但是对题目3的分析能够让我们更加理解jvm的运行机制。这段代码的返回值应该是多少?对Java语言熟悉的读者应该很容易说出答案:如果没有出现异常,返回值是1;如果出现了Exception异常,返回值是2;如果出现了Exception以外的异常,方法非正常退出,没有返回值。
分析一下题目3字节码的执行过程,从字节码的层面上看看为何会有这样的返回结果。字节码中第0~4行所做的操作就是将整数1赋值给变量x,并且将此时x的值复制一份副本到最后一个本地变量表的returnValue中如果这时没有出现异常,则会继续走到第5~9行,将变量x赋值为3,然后将之前保存在returnValue中的整数1读入到操作栈顶,最后ireturn指令会以int形式返回操作栈顶中的值,方法结束。 如果出现了异常,PC寄存器指针转到第10行,第10~20行所做的事情是将2赋值给变量x,然后将变量x此时的值赋给returnValue,最后再将变量x的值改为3。 方法返回前同样将returnValue中保留的整数2读到了操作栈顶。 从第21行开始的代码,作用是变量x的值赋为3,并将栈顶的异常抛出,方法结束。
注意:理解整个过程需要注意的一点是return x不是直接返回x的值返回的是x的一个副本,遇到return x时就local variable中保存一个x的副本,然后再返回 x的副本。
- Java进阶--Java中 i++完全解析
- Java进阶--static完全解析
- java进阶--Java中comparator及comparable完全解析
- Java进阶--ArrayDeque双端队列完全解析
- JAVA/C#中神奇的i=i++/i--,完全区别于C/C++
- java I/O解析
- Java进阶--深入解析hashmap
- Java - 集合框架完全解析
- Java注解相关完全解析
- Java - 集合框架完全解析
- java关键字final完全解析
- Java - 集合框架完全解析
- Java集合框架完全解析
- Java集合框架完全解析
- Java IO--File完全解析
- Java进阶(I/O系统)
- java中i++和++i
- java中i++和++i
- IPC进程间通信主题之信号量
- struts2+jdbc+oracle查询(模糊查询+页面查询)
- Python学习笔记——类属性和实例属性
- 夏令营面试经验——反思总结(更新ing)
- ARM裸机程序研究
- Java进阶--Java中 i++完全解析
- [java]类&抽象类&接口分别作为方法参数和返回值
- sizeof()与strlen()的区别
- 程序员日常工作英文20170608
- android项目中通过JNI调用Windows下的dll库
- spark 2.1 读取parquet外部表返回null
- 关系模型中的一些关键字的定义
- Opencv 中cv开头的函数和没有cv的区别,例如cvWaitkey()和waitKey()的区别
- Tomcat+Mysql web环境搭建 科协纳新网站以及优化设想