首先看代码
public static void main(String[] args) { int i = 0; i = i++; System.out.println(i); }
打印结果是0,而如果是i=++i,那么打印将会1。下面我们从字节码的角度来分析为什么是这样。
在讲字节码之前,先简单的了解下Java栈,在JVM中有这么一个数据结构叫Java栈,当线程启动的时候,会分配一块内存当做该线程的栈,每个栈由一系列的栈帧组成。每个栈帧对应一个方法,当线程执行方法时,就是栈帧出栈入栈的过程。
每个栈帧包含三部分数据:本地变量(参数+方法内的变量)、操作数栈和其它数据,本文主要涉及本地变量和操作数栈。
先看后置i++的实现
public static void main(String[] args) { int i = 0; i = i++; }
编译之后的字节码如下:
整个过程这样的:
可以发现变量在执行命令iinc 1,1的时候已经变成1了,但是istore_1又把变量所在位置覆盖成了0,所以执行完i = i++,i还是原来那个值。
下面,来看下前置++的实现:
字节码就不再解释了,整个过程实现如下:
和后置++不同的地方在于,在变量进入操作数栈之前,就先执行了iinc指令,所以进入操作数的值是加1后的值,最后写回的值也是最新值。
给出今天用到的字节码的含义
Bytecode |
Stack before->after |
Description |
iconst_0 |
->0 |
Loads the int value 0 onto the stack |
istore_1 |
value-> |
Store int value into variable 1 |
istore_2 |
value-> |
Store int value into variable 2 |
iinc |
No change |
Increment local variable #index by signed byte const |
iload_1 |
->value |
Loads an int value from variable 1 |
iadd |
value 1,value 2->result |
Adds 2 ints together |
说明两点需要注意的地方:
①iinc此操作对应于自加操作,并且该操作不对stack有任何改变;
②iadd操作过后只在stack中保留结果result。