起因是一个群友提出的问题,问以下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