int j=0;
for(int i=0;i<100;i++)
j=j++;
System.out.println(j);
如果你运行一下上面的程序,就会发现它打印的结果居然是0,但是如果把j=j++
换成j++
的话就会打印100.这是为什么呢?
如果你稍微有一点经验就会知道i++与++i的区别:
i++是在所有其他操作完成之后,自身加1。
++i是在自身加1之后,再去完成其他操作。
为什么++放在前面和后面区别这么大呢?
让我们从字节码的角度看看是怎么回事儿吧!
首先在这之前我们应该对java的栈有一点了解。
在jvm中有这么一个数据结构叫java栈,当线程启动的时候,会分配一块内存当做该线程的栈,每个栈由一系列的栈帧组成。每个栈帧对应一个方法,当线程执行方法时,就是栈帧出栈,入栈的过程。
每个栈帧包含三部分数据:本地变量(参数+方法内的变量)、操作数栈和其他数据,本文主要涉及本地变量和操作数栈。
先看看i++的实现:
public static void main(String[] args){
int i=0;
i=i++;
}
编译之后的字节码如下:
public static void main(java.lang.String[]);
flags: ACC_PUBLIC, ACC_STATIC
Code:
stack=1,locals=2,args_size=1
0:iconst_0 //把数值0 push到操作数栈
1:istore_1 //把操作数栈写回到本地变量第2个位置
2:iload_1 //把本地变量第2个位置的值push到操作数栈
3:iinc 1,1 //把本地变量表第2个位置加1
6:istore_1 //把操作数据栈写回本地变量第2个位置
7:return
LineNumberTable:
可以发现变量a在执行iinc的时候已经变成1了,但是istore_1又把变量a所在位置覆盖成0,所以执行完i=i++,i还是原来那个值。
另外,来看下++i的实现:
public static void main(java.lang.String[]);
flags: ACC_PUBLIC, ACC_STATIC
Code:
stack=1,locals=2,args_size=1
0:iconst_0 //把数值0 push到操作数栈
1:istore_1 //把操作数栈写回到本地变量第2个位置
2:iinc 1,1 //把本地变量表第2个位置加1
3:iload_1 //把本地变量第2个位置的值push到操作数栈
6:istore_1 //把操作数据栈写回本地变量第2个位置
7:return
LineNumberTable:
line 16:0
line 17:2
line 18:7
整个过程实现如下
和i++不同的地方在于,在变量进入操作数栈之前,就先执行了iinc指令,所以进入操作数的值是加1后的值,最后写回的值也是最新值。