懂得i++和++i
案例
代码1
package org.huangao.other.dome1;
import org.junit.Test;
public class Dome1 {
@Test
public void test1() {
int j = 0;
j = j++;
System.out.println(j);
}
@Test
public void test2() {
int j = 0;
j = ++j;
System.out.println(j);
}
}
结果
test1:0
test2:1
代码2
package org.huangao.other.dome1;
import org.junit.Test;
public class Dome1 {
@Test
public void test1() {
int j = 0;
// j = j++;
j++;
System.out.println(j);
}
@Test
public void test2() {
int j = 0;
// j = ++j;
++j;
System.out.println(j);
}
}
结果
test1:1
test2:1
可以初步分析是 j 值 对于 j++ 赋值的地方有所差异
分析
部分指令介绍
xload_n 将第(n+)个x类型本地变量推送至栈顶
xstore_n 将栈顶 x类型数值存入指定第(n+1)个本地变量
代码1的指令
D:jdk1.8.0_211injavap.exe -c org.huangao.other.dome1.Dome1
Compiled from "Dome1.java"
public class org.huangao.other.dome1.Dome1 {
public org.huangao.other.dome1.Dome1();
Code:
0: aload_0
1: invokespecial #1 // Method java/lang/Object."<init>":()V
4: return
public void test1();
Code:
0: iconst_0
1: istore_1
2: iload_1
3: iinc 1, 1
6: istore_1
7: getstatic #2 // Field java/lang/System.out:Ljava/io/PrintStream;
10: iload_1
11: invokevirtual #3 // Method java/io/PrintStream.println:(I)V
14: return
public void test2();
Code:
0: iconst_0
1: istore_1
2: iinc 1, 1
5: iload_1
6: istore_1
7: getstatic #2 // Field java/lang/System.out:Ljava/io/PrintStream;
10: iload_1
11: invokevirtual #3 // Method java/io/PrintStream.println:(I)V
14: return
}
Process finished with exit code 0
分析
首先我们先看test1 方法
iconst_0 代表将int型 0 推送至栈顶
istore_1 将栈顶 int 型数值存入第二个本地变量 即 j=0
iload_1 将第二个int 型本地变量 推送至栈顶 即 j=0
iinc 1,1 将局部变量表中1号元素自增1,此时j=1
istore_1 将栈顶 int 型数值存入第二个本地变量
getstatic
iload_1
invokevirtual
return
接下来看test2方法
iconst_0 将 int 型 0 推入栈顶
istore_1 将栈顶 int 型 数值存入第二个本地变量
iinc 1,1 将局部变量表1 号元素 自增1 ,此时j=1
iload_1 将第二个 int 型本地变量推送至栈顶
istore_1 将栈顶 int 型数值 存入第二个本地变量
getstatic
.....
代码2的指令
public void test1();
Code:
0: iconst_0
1: istore_1
2: iinc 1, 1
5: getstatic #2 // Field java/lang/System.out:Ljava/io/PrintStream;
8: iload_1
9: invokevirtual #3 // Method java/io/PrintStream.println:(I)V
12: return
public void test2();
Code:
0: iconst_0
1: istore_1
2: iinc 1, 1
5: getstatic #2 // Field java/lang/System.out:Ljava/io/PrintStream;
8: iload_1
9: invokevirtual #3 // Method java/io/PrintStream.println:(I)V
12: return
结论
本来局部变量表中的j已经完成了自增,但是在进行赋值的时候,是将操作栈中的数据弹出,直接进行覆盖操作
如果站在JVM的层次来说 对于 i++ 和 ++i
- i++ 是先被操作数栈拿去了(先执行了load指令),然后再在局部变量表中完成了自增,但是操作数栈中还是自增前的值
- 而++i是在局部变量表中完成了自增(先执行了innc指令),然后再被load进行了操作数栈,所以操作数栈中保存的是自增后的值
课后思考
@Test
public void test3() {
int i = 4;
int b = i++;
int a = ++i;
}
a 6
b 4
public void test3();
Code:
0: iconst_4 // 将常量4推送栈顶
1: istore_1 // 将栈顶元素4 存入第二个 本地变量 即i = 4
2: iload_1 // 将i=4 的值 推送到栈顶
3: iinc 1, 1 // 局部变量1号元素 +1 此时 i = 5
6: istore_2 // 将栈顶元素 存入第三个本地变量中即 b=4
7: iinc 1, 1 // 局部变量1号元素 +1 此时 i= 6
10: iload_1 // 将局部变量第二个元素推i=6 推送到栈顶
11: istore_3 // 将栈顶元素赋值到局部第三个变量即 a = 6
12: return
@Test
public void test3() {
int i = 4;
i = i++;
System.out.println(i);
i = ++i;
System.out.println(i);
}
4
5
public void test3();
Code:
0: iconst_4
1: istore_1
2: iload_1 //栈顶元素 4
3: iinc 1, 1 //局部变量 i+1 ;i=5
6: istore_1 //赋值局部元素 i = 栈顶元素 i = 4
7: getstatic #2 // Field java/lang/System.out:Ljava/io/PrintStream;
10: iload_1
11: invokevirtual #3 // Method java/io/PrintStream.println:(I)V
14: iinc 1, 1 // 局部变量i +1 ;i = 5
17: iload_1 // 5 进入栈顶
18: istore_1 // 栈顶元素5赋值给 i ; i = 5
19: getstatic #2 // Field java/lang/System.out:Ljava/io/PrintStream;
22: iload_1
23: invokevirtual #3 // Method java/io/PrintStream.println:(I)V
26: return
@Test
public void test3() {
int i = 4;
i = ++i;
System.out.println(i);
i = i++;
System.out.println(i);
}
5
5
public void test3();
Code:
0: iconst_4
1: istore_1
2: iinc 1, 1
5: iload_1
6: istore_1
7: getstatic #2 // Field java/lang/System.out:Ljava/io/PrintStream;
10: iload_1
11: invokevirtual #3 // Method java/io/PrintStream.println:(I)V
14: iload_1
15: iinc 1, 1
18: istore_1
19: getstatic #2 // Field java/lang/System.out:Ljava/io/PrintStream;
22: iload_1
23: invokevirtual #3 // Method java/io/PrintStream.println:(I)V
26: return