起因是一个群友提出的问题,问以下Java代码的输出的结果是什么?
int i = 1;
int j = ((++i) + (++i));
System.out.println(j);
众所周知,单个的++i
赋值是先计算+1然后计算,单个的i++
服之是先赋值后+1计算,但,多个操作复合起来就少有人知了。
接下来我们来研究一下。
经过测试,易得,结果是5。那么这个结果是怎么得到的呢?
首先我们将这段代码反编译一下,可得字节码如下:
// access flags 0x9
public static main([Ljava/lang/String;)V
// parameter args
L0
LINENUMBER 5 L0
ICONST_1
ISTORE 1
L1
LINENUMBER 6 L1
IINC 1 1
ILOAD 1
IINC 1 1
ILOAD 1
IADD
ISTORE 2
L2
LINENUMBER 7 L2
GETSTATIC java/lang/System.out : Ljava/io/PrintStream;
ILOAD 2
INVOKEVIRTUAL java/io/PrintStream.println (I)V
L3
LINENUMBER 12 L3
RETURN
L4
LOCALVARIABLE args [Ljava/lang/String; L0 L4 0
LOCALVARIABLE i I L1 L4 1
LOCALVARIABLE j I L2 L4 2
MAXSTACK = 2
MAXLOCALS = 3
先看这一段字节码:
L0
LINENUMBER 5 L0
ICONST_1
ISTORE 1
可以知道,变量i的指针是1,初始值为1
ICONST 是指使用了int的几个常量(<=5)的指针,同理还有LCONST对应long,DCONST对应double。
注意:这里的
_1
中的1,是指针,指向了常量。注意:非常量数值的数据压入操作会有所不同。
ISTORE 是将操作数栈顶上的int数值赋予本地变量,同理还有LSTORE,DSTORE等。
注意:ISTORE参数为指针
再来看来看这一段字节码:
L1
LINENUMBER 6 L1
IINC 1 1
ILOAD 1
IINC 1 1
ILOAD 1
IADD
ISTORE 2
首先将变量进行了IINC
操作,给变量+1,其次使用ILOAD
操作读取变量数值(当前为2),加入到操作数栈中。然后下面重复这个操作(当前为3)。最后调用了IADD
进行了加法操作,将操作数栈中的两个数值2+3的结果赋予变量2(就是代码中的j变量)。
最后输出结果5。
IINC
不影响操作数栈,但是会改对应变量的值,所以不需要重新使用ISTORE
压入。
同理我们再来测试i++,代码如下:
int i = 1;
int k = ((i++) + (i++) + (i++));
System.out.println(k);
字节码如下:
// access flags 0x9
public static main([Ljava/lang/String;)V
// parameter args
L0
LINENUMBER 9 L0
ICONST_1
ISTORE 1
L1
LINENUMBER 10 L1
ILOAD 1
IINC 1 1
ILOAD 1
IINC 1 1
IADD
ILOAD 1
IINC 1 1
IADD
ISTORE 2
L2
LINENUMBER 11 L2
GETSTATIC java/lang/System.out : Ljava/io/PrintStream;
ILOAD 2
INVOKEVIRTUAL java/io/PrintStream.println (I)V
L3
LINENUMBER 12 L3
RETURN
L4
LOCALVARIABLE args [Ljava/lang/String; L0 L4 0
LOCALVARIABLE i I L1 L4 1
LOCALVARIABLE k I L2 L4 2
MAXSTACK = 2
MAXLOCALS = 3
注意这一段字节码:
L1
LINENUMBER 10 L1
ILOAD 1
IINC 1 1
ILOAD 1
IINC 1 1
IADD
ILOAD 1
IINC 1 1
IADD
ISTORE 2
可以看到,这里是先使用ILOAD
操作读取数据到操作数栈中,然后再使用IINC
进行自增,这里就体现了i++
和++i
的区别。
至此,我们已经彻底了解了i++
和++i
。
但是,强烈不推荐这种写法,这种代码难以理解和维护。
参考文档
[1] javase11 jvm doc