从原理上分析 i = i++ 与 i = ++i
# 从原理上分析 i = i++ 与 i = ++i
# 1 前置知识
i = i++
与 i = ++i
与 k = i + ++i * i++
的运算涉及局部变量表和操作数栈。
- 自增、自减少操作都是直接修改变量的值,其实是在局部变量中完成,不经过操作数栈。
- 赋值=,最后计算。
- 赋值之前,临时的结果都是存储在操作数栈中。
- 实际的运算顺序看运算符的优先级。
# 2 i++
与++i
对于这样一段代码:
int i = 1;
i++;
1
2
2
或
int i = 1;
++i;
1
2
2
字节码文件均为
iconst_1 // 将立即数 1 压栈
istore_1 // 操作数栈顶 -> 局部变量表 1 位置
iinc 1 1 // 将局部变量表索引为1位置的值,加上1。
1
2
3
2
3
字节码前两行对应int i = 1;
,第三行对应自增操作,从中可以看出赋值操作需要操作数栈,而自增操作不需要操作数栈。
也就是说i++
与++i
在单独使用的时候是没有任何区别的,只有在配合=
赋值符号的时候才会有区别。
# 3 i = i++
与 i = ++i
对于下面两端代码:
public static void main(String[] args) {
int i = 1;
i = i++;
System.out.println(i); // 输出:1
}
1
2
3
4
5
2
3
4
5
public static void main(String[] args) {
int i = 1;
i = ++i;
System.out.println(i); // 输出:2
}
1
2
3
4
5
2
3
4
5
再次查看字节码:
对于i = i++;
iconst_1
istore_1
iload_1 <--
iinc 1 1 <--
istore_1
1
2
3
4
5
2
3
4
5
对于i = ++i;
iconst_1
istore_1
iinc 1 1 <--
iload_1 <--
istore_1
1
2
3
4
5
2
3
4
5
📖 可以看出只有第三行和第四行代码的顺序不同,这两行表示什么意思,为什么他们的不同导致最后的结果不同呢?下面看看注解。
📌 对于i = i++;
// 下面两行对应java代码 int i = 1
iconst_1 // 将立即数 1 压栈
istore_1 // 操作数栈顶 -> 局部变量表 1 位置
// 下面三行代码对应java代码 i = i++
iload_1 // 局部变量表 1 位置 -> 操作数栈顶,此时操作数栈顶为1
iinc 1 1 // 将局部变量表索引为1位置的值,加上1,也就是数值从1变成2
istore_1 // 操作数栈顶 -> 局部变量表 1 位置,也就是用操作数栈顶的1将局部变量表中的2覆盖掉,此时局部变量表表 1 位置的值为1.
1
2
3
4
5
6
7
8
2
3
4
5
6
7
8
具体过程如下:
📌 对于i = ++i;
// 下面两行对应java代码 int i = 1
iconst_1 // 将立即数 1 压栈
istore_1 // 操作数栈顶 -> 局部变量表 1 位置
// 下面三行代码对应java代码 i = ++i
iinc 1 1 // 局部变量表 1 位置的值 +1,也就是数值从1变成2
iload_1 // 局部变量表 1 位置 -> 操作栈顶,此时操作数栈顶为2
istore_1 // 操作数栈顶 -> 局部变量表 1 位置,也就是用操作数栈顶的2将局部变量表中的2覆盖
1
2
3
4
5
6
7
8
2
3
4
5
6
7
8
具体过程如下:
# 4 k = i + ++i * i++
下面思考一下这个问题:
public static void main(String[] args) {
int i = 2;
int k = i + ++i * i++;
System.out.println(i);
System.out.println(k);
}
1
2
3
4
5
6
2
3
4
5
6
💡 首先要知道 $2 + 3 \times 5$ 是如何计算的。
1 将2压入栈 2 将3压入栈 3 将5压入栈 4 弹出3和5 5 计算 $3 \times 5$,将15压入栈 6 弹出2和15 7 计算 $2 + 15$,将17压入栈
int k = i + ++i * i++;
与上面的计算过程一样。
🔎 从字节码角度看看
iconst_2 // 将立即数 2 压栈
istore_1 // 操作数栈顶 -> 局部变量表 1 位置,现在局部变量表 1 的位置的数值为2,也就是i=2
iload_1 // 局部变量表 1 位置 -> 操作栈顶,此时操作数栈顶为2
iinc 1, 1 // 局部变量表 1 的位置的数值加1,变成3
iload_1 // 局部变量表 1 位置 -> 操作栈顶,此时操作数栈顶为3,此时栈中元素有 (2, 3)
iload_1 // 局部变量表 1 位置 -> 操作栈顶,此时操作数栈顶为3 ,此时栈中元素有 (2, 3, 3)
iinc 1, 1 // 局部变量表 1 的位置的数值加1,变成4
imul // 取出栈顶的两个元素相乘,结果再放回栈顶,此时栈中元素有 (2, 9)
iadd // 取出栈顶的两个元素相加,结果再放回栈顶,此时栈中元素有 (11)
istore_2 // 操作数栈顶 -> 局部变量表 2 位置,现在局部变量表 2 的位置的数值为11,也就是k=11
1
2
3
4
5
6
7
8
9
10
2
3
4
5
6
7
8
9
10
所以上面这行代码允许之后的结果:
public static void main(String[] args) {
int i = 2;
int k = i + ++i * i++;
System.out.println(i); // 输出:4
System.out.println(k); // 输出:11
}
1
2
3
4
5
6
2
3
4
5
6
编辑 (opens new window)
上次更新: 2023/08/20, 21:21:52