Analyze `i++` and `++i` statements in Java

Analysis in Java i++, ++ithe statement

1.Java in i++and ++iintroduce

  • ++ is an arithmetic operator

  • There are i++ and ++i in many languages. In some languages, i++ and ++i can be used as lvalues ​​and rvalues, but in Javalanguages, these two statements can only be used as rvalues, not as left values. value. At the same time, they can all be executed as an independent instruction.

    int i = 2;
    int j1 = i++; // 正常编译和运行
    int j2 = ++i; // 正常编译和运行
    i++; // 正常编译和运行
    ++i; // 正常编译和运行
    i++ = 2; // 编译不通过
    ++i = 2; // 编译不通过
    
  • For the auto-increment variable itself, whether ++ is before or after, the auto-increment variable itself will increase by 1; but for an auto-increment expression, the result of ++ before or after is different:

    public class Test_20200623 {
          
          
        public static void main(String[] args) {
          
          
            int i = 2;
            int j1 = i++;
            //先赋值给j,后自增
            System.out.println("j1=" + j1); // 输出 j1=2
            System.out.println("i=" + i); // 输出 i=3
    				//先自增,后赋值给j
            int j2 = ++i;
            System.out.println("j2=" + j2); // 输出 j2=4
            System.out.println("i=" + i); // 输出 i=4
        }
    }
    

2.Java in i++and ++ithe principles underlying implementation

  • View the assignment process by analyzing the bytecode of the source code

  • Source code:

public class Test_20200623_2 {
    
    
    public static void main(String[] args) {
    
    

    }

    public  void test1() {
    
    
        int i = 2;
        int j = i++;
    }

    public  void test2() {
    
    
        int i = 2;
        int j = ++i;
    }
}
  • Compile the source codejavac Test_20200623_2.java
  • View bytecode through javap commandjavap -c Test_20200623_2.class
Compiled from "Test_20200623_2.java"
public class Test_20200623_2 {
    
    
  public Test_20200623_2();
    Code:
       0: aload_0
       1: invokespecial #1            // Method java/lang/Object."<init>":()V
       4: return

  public static void main(java.lang.String[]);
    Code:
       0: return

  public void test1();
    Code:
       0: iconst_2								// 生成整数2
       1: istore_1								// 将整数2赋值给1号存储单元(即变量i)
       2: iload_1									// 将1号存储单元的值加载到数据栈(此时i=2,栈顶值为2)
       3: iinc          1, 1			// 1号存储单元的值+1(此时 i=3)
       6: istore_2								// 将数据栈顶的值(2)取出来赋值给2号存储单元(即变量j,此时i=3,j=2)
       7: return								  // 返回时:i=3,j=2

  public void test2();
    Code:
       0: iconst_2								// 生成整数2
       1: istore_1								// 将整数2赋值给1号存储单元(即变量i)
       2: iinc          1, 1			// 1号存储单元的值+1(此时 i=3)
       5: iload_1									// 将1号存储单元的值加载到数据栈(此时i=3,栈顶值为3)
       6: istore_2								// 将数据栈顶的值(3)取出来赋值给2号存储单元(即变量j,此时i=3,j=3)
       7: return									// 返回时:i=3,j=3
}
  • The key point in the above bytecode is which of the following two steps should be done first:

    • The value of No. 1 storage unit +1
    • Load the value of No. 1 memory cell to the data stack
  • For j=i++; is to first load the value of the No. 1 storage unit into the data stack (2), and then add the value of the No. 1 storage unit +1 (i=3), so when the value at the top of the data stack (2) is taken out and When assigning value to j, j=2.

  • For j=++i; is to first add the value of No. 1 storage unit +1 (i=3), and then load the value of No. 1 storage unit to the data stack (3), so when the value at the top of the data stack (3) is taken out and When assigning value to j, j=3.

3. Java neutrali=i++

  • Source code:
public class Test_20200624_1 {
    
    
    public static void main(String[] args) {
    
    
        int i = 2;
        i = i++;

        System.out.println("i=" + i); // 输出 i=2
    }
}
  • Bytecode analysis:
Compiled from "Test_20200624_1.java"
public class Test_20200624_1 {
    
    
  public Test_20200624_1();
    Code:
       0: aload_0
       1: invokespecial #1                  // Method java/lang/Object."<init>":()V
       4: return

  public static void main(java.lang.String[]);
    Code:
       0: iconst_2													    // 生成整数2
       1: istore_1												      // 将整数2赋值给1号存储单元(即变量i,i=2)				
       2: iload_1														    // 将1号存储单元的值加载到数据栈(此时 i=2,栈顶值为2)
       3: iinc          1, 1								    // 1号存储单元的值+1(此时 i=3)
       6: istore_1															// 将数据栈顶的值(2)取出来赋值给1号存储单元(即变量i,此时i=2)
       7: getstatic     #2                  // Field    java/lang/System.out:Ljava/io/PrintStream;  // 下面是打印到控制台指令
      10: new           #3                  // class java/lang/StringBuilder
      13: dup
      14: invokespecial #4                  // Method java/lang/StringBuilder."<init>":()V
      17: ldc           #5                  // String i=
      19: invokevirtual #6                  // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
      22: iload_1
      23: invokevirtual #7                  // Method java/lang/StringBuilder.append:(I)Ljava/lang/StringBuilder;
      26: invokevirtual #8                  // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
      29: invokevirtual #9                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V
      32: return
}
  • The analysis shows that the key lies in understanding the data stack and storage unit

  • i = i++It is to load the value (i=2) in the storage unit No. 1 (identified by variable i) into the data stack, and then change the value of the storage unit No. 1 by +1 (i=3), and the data stack (stored is 2) Assign the value in No. 1 storage unit (i=2).

  • If it is i = i++changed i = ++i, then first change the value of No. 1 storage unit + 1 (i=3), and then load the value (i=3) in No. 1 storage unit (identified by variable i) into the data stack, and finally The value in the data stack (stored in 3) is assigned to the No. 1 storage unit (i=3). That is equivalent to ++ior i++.

4. More examples about i++and++i

4.1

int a = 2; 
int b = (3 * a++) + a;   //b=(3*2)+3
System.out.println(b);   //  结果:9

4.2

int a = 2; 
int b = a + (3 * a++);  //2+(3*2)
System.out.println(b); // 结果:8

4.3

int i = 1;
int j = 1;
int k = i++ + ++i + ++j + j++;     //1+3+2+2
System.out.println(k);            // 结果:8

4.4

int a = 0;
int b = 0;
a = a++;
b = a++;
System.out.println("a = " + a + ", b = " + b); // a = 1, b = 0

5. ++iData confusion caused by operations in a multithreaded environment in Java

  • The reason for the confusion is that the ++ioperation is not atomic.

  • Although Javait ++iis a statement in the middle , the bytecode level also corresponds to iincthis JVM instruction, but from the lowest level of the CPU level, the ++ioperation can be roughly divided into the following three instructions:

    • Number of orders
    • Accumulate
    • storage
  • x = 10;        //语句1  原子性操作
    y = x;         //语句2  非原子性操作
    x++;           //语句3	非原子性操作
    x = x + 1;     //语句4	非原子性操作
    
  • One of the instructions can be guaranteed to be an atomic operation, but the three instructions together are not, which results in the ++istatement not being an atomic operation.

  • If the variable i is modified with volatile, there is no guarantee that ++i is an atomic operation. As for the reason, you can refer to: Java concurrent programming: volatile keyword analysis . If you want to ensure the atomicity of the accumulation operation, you can take the following methods:

    • Put ++i in the synchronization block, which can be synchronized or an exclusive lock in JUC (such as ReentrantLock, etc.).
    • Use atomic (Atomic) class to replace ++i, which class is used depends on the variable type. If i is plastic, use the AtomicInteger class, where AtomicInteger#addAndGet() corresponds to the ++i statement, but it is an atomic operation.

appendix

javap command

  • Disassemble one or more class files. Disassembles one or more class files.
  • This javapcommand disassembles one or more 类文件. The output depends on the options used. If no options are used, the javapcommand will print the package, the protected and public fields, and the methods of the class passed to it. This javapcommand prints its output to stdout. Description:The javapcommand disassembles one or more class files. The output depends on the options used. When no options are used, then the javapcommand prints the package, protected and public fields, and methods of the classes passed to it. The javapcommand prints its output to stdout.
  • View the usage of javap in the terminal javap -help:
用法: javap <options> <classes>
其中, 可能的选项包括:
  -? -h --help -help               输出此帮助消息
  -version                         版本信息
  -v  -verbose                     输出附加信息
  -l                               输出行号和本地变量表
  -public                          仅显示公共类和成员
  -protected                       显示受保护的/公共类和成员
  -package                         显示程序包/受保护的/公共类
                                   和成员 (默认)
  -p  -private                     显示所有类和成员
  -c                               对代码进行反汇编
  -s                               输出内部类型签名
  -sysinfo                         显示正在处理的类的
                                   系统信息(路径、大小、日期、SHA-256 散列)
  -constants                       显示最终常量
  --module <模块>, -m <模块>       指定包含要反汇编的类的模块
  --module-path <路径>             指定查找应用程序模块的位置
  --system <jdk>                   指定查找系统模块的位置
  --class-path <路径>              指定查找用户类文件的位置
  -classpath <路径>                指定查找用户类文件的位置
  -cp <路径>                       指定查找用户类文件的位置
  -bootclasspath <路径>            覆盖引导类文件的位置
  --multi-release <version>        指定要在多发行版 JAR 文件中使用的版本
  • Common commands: javap -c ClassName.classdisassemble a class file

reference:

1. In- depth understanding of i++ and ++i statements in Java

2. The most easy-to-understand detailed explanation of i++ and ++i

3.Java Platform, Standard Edition Tools Reference

4. Talk about the javap command

5. Java concurrent programming: volatile keyword analysis

Guess you like

Origin blog.csdn.net/szw_yx/article/details/106970713