在学完jvm的一些知识后,发现有些令人云山雾罩的问题,逐渐变得条理清晰。首先,在这里保证,看完以下解释,让你彻底明白i++和++i的原理,而不是停留在先赋值后运算的浅层次的理解。因为涉及底层,有些东西可能晦涩难懂,但难能可贵。但柳岸花明又一村,相信你可以。加油。
一、相关知识
以下介绍的知识是理解的必备装备,在打“boss”前最后还是捡一些武器。
虚拟机栈:java虚拟机运行时数据区的一部分,一个线程对应一个栈,栈控制着方法的调用。
栈帧:虚拟机栈的栈元素,每次调用一个方法,就会创建与方法对应的栈帧,并将栈帧入栈。
举个栗子:
public class Main {
public static void main(String[] args) {
int i = 1;
int j = i++;
}
}
栈帧结构(详细内容参阅:http://www.cnblogs.com/noKing/p/8167700.html)
局部变量表: 是一片逻辑连续的内存空间,可以理解为类似数组的结构,具有像slot0、slot1这样的索引。它是用来存放方法参数和方法内部定义的局部变量。
是按参数和局部变量在代码中出现的顺序而依次对应slot0、slot1、
slot2的存储位置,为了更直观写了slot0(args),实际只有索引slot0,没有args。
知识拓展slot:slot是虚拟机为局部变量表分配内存所使用的最小单位。对于byte,char,float,int,shot,boolean,reference和returnAddress等长度不超过32位的数据类型,每个局部变量占1个Slot,而double与long这两种64位的数据类型而需要2个Slot来存放
操作数栈: Java虚拟机提供指令来让操作数栈对一些数据进行入栈操作,比如可以把局部变量表里的数据、实例的字段、常量值等数据入栈。
栈帧的大小在编译时就确定,局部变量表的大小、操作数栈的大小也是在编译时就确定大小的。
二、底层执行过程
(1) 反编译得到main方法对应的字节码指令cmd(windows命令行):javap -v Main.class
(注:stack即操作数栈,locals即局部变量表)
接下来我们依次运行八条指令,索引是从0开始的。
以下大多数情况i开头的i代表int类型
0: iconst_1
将int型常量值1入操作数栈
1: istore_1
出栈(操作数栈),并把出栈的值保存到局部变量表索引为1(从_1得知索引为1)的位置
2:iload_1
将索引为1即slot1处的值拷贝一份入栈
3: iinc 1, 1
*将索引为slot1处的值 加1
iinc index, constvalue
index:由执行++运算的变量在局部变量表的位置决定,本测试中为i++,而i对应的局部变量表的位置为slot1,所以index=1.
contstvalue: 由于++运算为变量加1,所以constvalue恒为一.
6: istore_2
出栈,并把值保存到索引值为2即slot2处
int i = 1 ;
int j = i++;
最终执行结果: i = 2; j=1;
7: return
方法执行完毕,对应栈帧出栈
那么
++i
是如何进行的呢?
Main.java
public class Main {
public static void main(String[] args) {
int i = 1;
int j = ++i;
}
}
字节码指令
执行流程:
看到这里,参不多已经可以下山了。没有一点小激动吗?别急,临走前,在给你本“葵花宝典”!
《葵花宝典》–对比图
写在结尾:纯手工制作,没有复制粘贴。您的赞是对我最大的鼓励!