之前在做findbugs的时候经常会查看字节码,都是一知半解的
那天看到一个问题,刚好可以从字节码来解释
这些题目常见于面试题,通常要是谁敢这么写代码,完全是在找死。。
- public class Test {
- public static void main(String[] args) {
- int i=0;
- i=i++;
- System.out.println(i);
- }
- }
大家说这里为什么会输出0呢,这里我们从字节码的角度解释
通过反编命令javap –c classname来获得字节码
- Compiled from "Test.java"
- public class Test extends java.lang.Object{
- public Test();
- Code:
- 0: aload_0
- 1: invokespecial #8; //Method java/lang/Object."<init>":()V
- 4: return
- public static void main(java.lang.String[]);
- Code:
- 0: iconst_0
- 1: istore_1
- 2: iload_1
- 3: iinc 1, 1
- 6: istore_1
- 7: getstatic #16; //Field java/lang/System.out:Ljava/io/PrintStream;
- 10: iload_1
- 11: invokevirtual #22; //Method java/io/PrintStream.println:(I)V
- 14: return
- }
这里我们主要看main方法里面的,附上解释
- 0: iconst_0 这里是声明了一个常量0,并压入堆栈
- 1: istore_1 将常量0弹出赋值给本地便量表index为1位置的变量(也就是i)
- 以上完成了int i=0
- 2: iload_1 把本地变量表index为1的位置的值(也就是i的值)压入堆栈
- 3: iinc 1, 1 i的值加1也就是将本地便量表index为1的位置的值加1,但是相应的堆栈里面没有加
- 上完成了i++
- 6: istore_1 将堆栈里面的0弹出赋值给i(也就是0赋值给了i)
- 里完成了i=i++
总的来说是因为在执行++的之前会先把i的值放入栈保存,然后在执行++,但是++仅改变了值不会改变堆栈里面的值,所以当执行完毕后,弹出堆栈的值仍然是0,所以i最后还是0
jvm的字节码就像C的汇编,会在根本上解决很多问题,另外熟悉也会提高我们代码效率
以下代码X可为a、d、f、l或者i。
其中a指引用、b指布尔类型、c指字符、d指双精度类型、f指浮点类型、i指整型、l指长整型、s指短整型。
堆栈操作。
pop、pop2:将堆栈的值弹出。pop2用来弹出64位的值(long、double),pop弹出32位的。
const_null将null的引用推送至堆栈。
bipush将单字节的常量值(-127~128)推送至堆栈。
sipush将一个短整型类型的常量值(-32K~32K)推送至堆栈。
ldc将常量值从常量池中推送至堆栈。
Xload,是将一个本地的X类型(参数或变量)推送至堆栈。
Xstore,将堆栈顶端的X类型的值弹出并放入本地分片中。
Xconst_Y 用来将X类型的常量Y值推送至堆栈。如,iconst_0就是将整数常量0推送至堆栈中。
分支与控制流。
nop,什么也不做。
if(条件),条件可以是null(==null)、notnull(!=null)、eq、ne、gt、lt、_icmpeq、_icmpne……。
goto N。跳至指令号N。
return和Xreturn。X可以为a、d、f、l或者i,从当前调用方返回,将堆栈顶端作为X类型返回。