在面试中经常会被问到 ++i 和 i++相关的问题
i++
首先先看一段代码
public class Test {
public static void main(String[] args) {
foo();
}
public static void foo() {
int i = 0;
for (int j = 0; j < 50; j++) {
i = i++;
}
System.out.println(i);
}
}
执行上述代码输出结果是0,而不是50
0
对应字节码
public class com.yxzapp.Test {
public com.yxzapp.Test();
Code:
0: aload_0
1: invokespecial #1 // Method java/lang/Object."<init>":()V
4: return
public static void main(java.lang.String[]);
Code:
0: invokestatic #2 // Method foo:()V
3: return
public static void foo();
Code:
0: iconst_0
1: istore_0
2: iconst_0
3: istore_1
4: iload_1
5: bipush 50
7: if_icmpge 21
10: iload_0 // 把局部变量下标0的变量加载到操作数栈上
11: iinc 0, 1 // 对局部变量表表为0的int变量直接加1,但是这 个时候栈顶元素没有变化,还是0
14: istore_0 // 将栈顶元素出栈赋值给局部变量下标0变量 也就 是i。 此时 局部变量 i 又被赋值为0,前面 iinc 指令对i 的加一操作一样被覆盖
15: iinc 1, 1
18: goto 4
21: getstatic #3 // Field java/lang/System.out:Ljava/io/PrintStream;
24: iload_0
25: invokevirtual #4 // Method java/io/PrintStream.println:(I)V
28: return
}
在字节码层面是先把 i 加载到操作数栈上,随后才对局部变量 i 执行加一操作 , 留在操作数栈顶的还是 i 的旧值。则这个变量得到的是 i 加一之前的值
++i
public class Test {
public static void main(String[] args) {
foo();
}
public static void foo() {
int i = 0;
for (int j = 0; j < 50; j++) {
i = ++i;
}
System.out.println(i);
}
}
对应字节码
public class com.yxzapp.Test {
public com.yxzapp.Test();
Code:
0: aload_0
1: invokespecial #1 // Method java/lang/Object."<init>":()V
4: return
public static void main(java.lang.String[]);
Code:
0: invokestatic #2 // Method foo:()V
3: return
public static void foo();
Code:
0: iconst_0
1: istore_0
2: iconst_0
3: istore_1
4: iload_1
5: bipush 50
7: if_icmpge 21
10: iinc 0, 1 // 对局部变量表表为0的int变量直接加1,
13: iload_0 // 把局部变量下标0的变量加载到操作数栈上
14: istore_0 // 将栈顶元素出栈赋值给局部变量下标0变量
15: iinc 1, 1
18: goto 4
21: getstatic #3 // Field java/lang/System.out:Ljava/io/PrintStream;
24: iload_0
25: invokevirtual #4 // Method java/io/PrintStream.println:(I)V
28: return
}
可以看出 i= ++i , 先对局部变量表下标为0的变量加 1 ,然后才把它加载到操作数栈上,随后又从操作数栈上出栈赋值给局部变量表中下标为 0 的变量
总结 :
- i++ 即后加加,原理是:先自增,然后返回自增之前的值、
- ++i 即前加加,原理是:先自增,然后返回自增之后的值