通过示例从字节码的角度研究++i和i++底层发生的事情。参考《jvm字节码从入门到精通》
i++字节码分析
package com.java.bt;
public class BTTest {
public static void test() {
int i = 0;
for (int j = 0 ; j < 50; j++) {
i = i++;
}
System.out.println(i);
}
public static void main(String[] args) {
test();
}
}
此段代码运行之后输出0。
通过javap -v BTTest
命令对.java文件,反编译获得字节码。
分析:
- 其中0~1行对应
int i = 0;
iconst_0
把0压入操作数栈。
istore_0
将栈顶元素存入局部变量表(LocalVariableTable)0槽(slot)中。 - 其中2~3行对应
int j = 0;
同理。 - 第4~7对应
j < 50;
判断是否跳出循环。
iload_1
将局部变量表槽1中的元素放入栈顶。
bipush 50
将50压入栈顶。
if_icmpge 21
将栈顶两个元素取出进行比较,如果 j >= 50 则跳转到第21行。 - 第7~14行对应
i = i++
。
iload_0
将i读取到栈顶。
iinc 0, 1
iinc 0, 1指令可以直接让局部变量表槽0位置的数+1,而不用先把数字放到操作数栈中。
istore_0
此时将栈顶的元素存入槽0中(覆盖了增加1以后的i)。 - 第15行对应
j++
。
iinc 1, 1
直接让槽1中的变量(j)增加1。 goto 4
跳转到第四行,for循环体结束。- 21-25行对应
System.out.println(i)
getstatic #2
读入静态变量System.out(变量名存在s常量池#2位置)
iload_0
将i压入栈中。
invokespecialvirtual #3
调用栈顶对象的println方法。
++i字节码分析
将上述代码稍作修改,如下:
package com.java.bt;
public class BTTest {
public static void test() {
int i = 0;
for (int j = 0 ; j < 50; j++) {
i = ++i;
}
System.out.println(i);
}
public static void main(String[] args) {
test();
}
}
运行结果为:50
重新编译再查看其字节码如下:
分析:
该字节码唯一的差别在10~14行。
- ++i先执行
iinc 0, 1
将槽0的变量i增加1; - 再通过
iload_0
将其取出; - 通过
istore_0
将其赋值给i。
因此每一轮循环i都加1,最终结果为50。
i = i++ + ++i
示例代码如下:
package com.java.bt;
public class BTTest {
public static void main(String[] args) {
int i = 0;
i = i++ + ++i;
System.out.println(i);
}
}
此段代码似乎没什么意义,但是通过分析其字节码可以得到输出的结果,字节码如下:
分析:
- 第0~1行中
istore_1
表示将0存入槽1中。(因为槽0中存的是main方法的参数) - 第2-11行是主要关注的,通过字节码可以清楚的分析得到最终槽1中的变量(i)值为2。
除此之外还可以通过字节码分析i = ++i + ++i
以及i = i++ + i++
等表达式的结果。