Java中变量自增:a++和++a的区别

public static void main(String[] args) {
    int a=10;
    int b=a++;
    int c=++a;
}

在最开始学习Java时我们都知道,第二行代码:int b=a++,是先把变量赋值给b,然后再执行自增。第三行代码:int b=++a;是先对a执行自增,再赋值给c。

下面我们看Java编译的字节码文件来看一下在底层,栈内部这个区别是具体怎么体现的。

经过编译之后会生成.class的字节码文件,我们可以使用以下命令查看字节码文件

javap -v *.class

得到以下结果

Classfile /Users/mengao/IdeaProjects/JavaBook/target/classes/_05_jvm相关/_02_.class
  Last modified 2022-2-12; size 454 bytes
  MD5 checksum 5c2f282df67cce412e133bdf41f398da
  Compiled from "_02_.java"
public class _05_jvm相关._02_
  minor version: 0
  major version: 52
  flags: ACC_PUBLIC, ACC_SUPER
Constant pool:
   #1 = Methodref          #3.#21         // java/lang/Object."<init>":()V
   #2 = Class              #22            // _05_jvm相关/_02_
   #3 = Class              #23            // java/lang/Object
   #4 = Utf8               <init>
   #5 = Utf8               ()V
   #6 = Utf8               Code
   #7 = Utf8               LineNumberTable
   #8 = Utf8               LocalVariableTable
   #9 = Utf8               this
  #10 = Utf8               L_05_jvm相关/_02_;
  #11 = Utf8               main
  #12 = Utf8               ([Ljava/lang/String;)V
  #13 = Utf8               args
  #14 = Utf8               [Ljava/lang/String;
  #15 = Utf8               a
  #16 = Utf8               I
  #17 = Utf8               b
  #18 = Utf8               c
  #19 = Utf8               SourceFile
  #20 = Utf8               _02_.java
  #21 = NameAndType        #4:#5          // "<init>":()V
  #22 = Utf8               _05_jvm相关/_02_
  #23 = Utf8               java/lang/Object
{
  public _05_jvm相关._02_();
    descriptor: ()V
    flags: ACC_PUBLIC
    Code:
      stack=1, locals=1, args_size=1
         0: aload_0
         1: invokespecial #1                  // Method java/lang/Object."<init>":()V
         4: return
      LineNumberTable:
        line 9: 0
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0       5     0  this   L_05_jvm相关/_02_;

  public static void main(java.lang.String[]);
    descriptor: ([Ljava/lang/String;)V
    flags: ACC_PUBLIC, ACC_STATIC
    Code:
      stack=1, locals=4, args_size=1
         0: bipush        10
         2: istore_1
         3: iload_1
         4: iinc          1, 1
         7: istore_2
         8: iinc          1, 1
        11: iload_1
        12: istore_3
        13: return
      LineNumberTable:
        line 11: 0
        line 12: 3
        line 13: 8
        line 18: 13
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0      14     0  args   [Ljava/lang/String;
            3      11     1     a   I
            8       6     2     b   I
           13       1     3     c   I
}
SourceFile: "_02_.java"

其中的关键部分是main方法里执行的代码:

         0: bipush        10
         2: istore_1
         3: iload_1
         4: iinc          1, 1
         7: istore_2
         8: iinc          1, 1
        11: iload_1
        12: istore_3
        13: return

添加注释:

         0: bipush        10     # 把变量10放入操作数栈
         2: istore_1             # 把操作数栈里的数存储到槽位编号1的变量上,也就是赋值给变量a,此后a=10
         3: iload_1              # 把变量a的值放入操作数栈
         4: iinc          1, 1   # 对槽位编号1的变量执行+1,此后a=11
         7: istore_2             # 把操作数栈里的内容赋值给槽位编号2的变量上,也就是赋值给变量b,此后b=10
         8: iinc          1, 1   # 对槽位编号1的变量执行+1,此后a=12
        11: iload_1              # 把槽位编号为1的内容,存储到操作数栈
        12: istore_3             # 把操作数栈里栈顶的值赋值给槽位编号为2的变量,也就是赋值给变量c,此后c=12
        13: return               # 结束

可以看到在经过编译后的字节码文件里的指令集中,就能发现在栈内部的区别。

a++是先把变量放到操作数栈,然后对变量进行++,这时操作数栈存储的还是原来的值,最后把操作数栈里的值赋值给变量b

++a是先把变量a,在槽位里直接+1,然后再把槽位变量a放到操作数栈顶,最后把这个操作数栈顶的值赋值给变量b。