深入理解i++和++i

看一段代码:


public static void main(String[] args){

    int i = 0;

    i = i ++;

}

i最后的结果为0,如果换成++i,i最后的结果就是1;

这个现象我相信很多人很容易解释。

1、前置++是将自身加1的值赋值给新变量,同时自身也加1。

2、后置++是将自身的值赋给新变量,然后才自身加1。

这样就结束了吗?当然不是!有没有人去证实过这个现象?JVM又是如何存储这个变量和改变这个变量的呢?

想到这里,我就开始看它的字节码文件

image

iconst_0   //把数值0推到操作数栈赋值给i

istore_1   // 把操作数栈的i值写回到局部变量第2个位置

iload_1   // 把本地变量第2个位置的值推到到操作数栈,因为要赋值给i

iinc 1, 1   // 把局部变量表第2个位置的值加1 

 istore_1   // 把操作数据栈写局部变量第2个位置

可以发现定义变量i为0是将0 push到操作数栈赋值给i,然后将i存入本地变量,下面进行i++操作的时候先从本地变量拿到i进入操作数栈,然后本地变量表的i加一,然后将赋值之后i为0的i写回本地变量。

下面看看前置++的实现:

image

不同的就是,现在本地变量表加一之后再载入数栈。

下面来普及一下java栈方面知识。

JVM中有一个数据结构叫虚拟机栈,当线程启动的时候,会分配一块内存当作该线程的栈,也就是说java是线程私有的,它的生命周期与线程相同,虚拟机栈描述的是java方法执行的内存模型:每个方法在执行的同时会创建一个栈帧用于储存局部变量、操作数栈、动态链接、方法出口等。每一个方法从调用直至执行完成的过程,就对应一个栈帧在虚拟机栈中入栈到出栈的过程。

这里重点介绍一下栈帧、局部变量和操作数栈:

栈帧:是用于支持虚拟机进行方法调用和方法执行的数据结构。对于执行引擎来说,活动线程中,只有栈顶的栈帧是有效的,称为当前栈帧,这个栈帧所关联的方法称为当前方法。执行引擎所运行的所有字节码指令都只针对当前栈帧进行操作。

局部变量:局部变量表是一组变量值存储空间,用于存放方法参数和方法内部定义的局部变量。在Java程序被编译成Class文件时,就在方法的Code属性的max_locals数据项中确定了该方法所需要分配的。局部变量表的容量以变量槽(Slot)为最小单位,32位虚拟机中一个Slot可以存放一个32位以内的数据类型(boolean、byte、char、short、int、float、reference和returnAddress八种)。reference类型虚拟机规范没有明确说明它的长度,但一般来说,虚拟机实现至少都应当能从此引用中直接或者间接地查找到对象在Java堆中的起始地址索引和方法区中的对象类型数据。

操作数栈:Java虚拟机的解释执行引擎被称为"基于栈的执行引擎",其中所指的栈就是指-操作数栈。操作数栈也常被称为操作栈。和局部变量区一样,操作数栈也是被组织成一个以字长为单位的数组。但是和前者不同的是,它不是通过索引来访问,而是通过标准的栈操作—压栈和出栈—来访问的。比如,如果某个指令把一个值压入到操作数栈中,稍后另一个指令就可以弹出这个值来使用。虚拟机在操作数栈中存储数据的方式和在局部变量区中是一样的:如int、long、float、double、reference和returnType的存储。对于byte、short以及char类型的值在压入到操作数栈之前,也会被转换为int。


建议:对一些自己无法理解或者解释但又想搞明白的同学,希望多去看看字节码文件。但是你打开class文件会发现是一大堆的二进制文件,到底怎么看呢?下面我就分享下如何看class的字节码文件:

image
image

点击加号:

image
image

温馨提示:先编译再运行哦

你可能感兴趣的:(深入理解i++和++i)