地点:某群,事件:某群友发了一张图,大致就是问int i =1; i = i++;
中 i 的值是多少
我说简单啊,不就是i = 2
吗?结果一到Java中运行,光速打脸 – -|
展现了我的愚昧无知之后,立马百度科普一下:JVM中的局部变量自增。
然而我不了解JVM外加有点蠢,还是不理解… 于是,自己编译了一个样本,来分析 jvm bytecode
“鲜明”对比
public class Main { public static void main(String[] args) { int i = 1; i = i++; System.out.println(i); } }
javap -c xx.class
,打印出jvm的字节码
百度了查了查jvm字节码指令表,写了份分析注释
public class com.helloworld.Main { public com.helloworld.Main(); 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_1 // 1 压入操作数栈帧顶 1: istore_1 // 取操作数栈帧顶数据(1) 压入 局部变量表索引为1的变量中 2: iload_1 // 取局部变量表索引为1的变量, 压入操作数栈帧顶 3: iinc 1, 1 // 取局部变量表索引为1的变量 进行自增操作(++),这里需要注意的是iinc指令直接对局部变量进行操作 6: istore_1 // 取操作数栈帧顶数据(1), 压入局部变量表索引为1的变量中 // 7: getstatic #7 // Field java/lang/System.out:Ljava/io/PrintStream; 10: iload_1 11: invokevirtual #13 // Method java/io/PrintStream.println:(I)V 14: return }
原因明显是iinc
操作的是变量本身,并没有入栈(操作数栈),于是在赋值时的值依旧是1,那么为什么没有入栈呢?
下面来看看Java中的最简单的自增运算和赋值运算
对比字节码后,可以发现两者不同点在于:Jvm中iinc
指令操作的直接是局部变量本身,而Jvm的赋值是入栈->操作->出栈
根据运算规则,可以知道Java首先执行自增(i++),而后进行赋值,但由于自增并不是基于操作数栈的操作,在自增执行完成后,此时的操作数栈顶数据依旧是变量入栈时的数据(1),所以i = i++
恒等于原先的值(1)
延展
那么在以C为代表的非虚拟机运行的语言中,自增与赋值又是怎么样的一种表现?简单分析一下C的汇编
发现不管是自增运算或是赋值运算,C都是利用寄存器实现(至于i++与i=i+1为什么没有区别,应该是编译器优化掉了)
END
某群
没学过Java的我:!!!Java这么骚?学到了学到了…
某群内大佬:….这难道不是基础?
发表回复