java字節碼

之前在做findbugs的時候經常會查看字節碼,都是一知半解的
那天看到一個問題,剛好可以從字節碼來解釋

這些題目常見於面試題,通常要是誰敢這麼寫代碼,完全是在找死。。

Java代碼 複製代碼
  1. public class Test {   
  2.     public static void main(String[] args) {   
  3.         int i=0;   
  4.         i=i++;   
  5.         System.out.println(i);   
  6.     }   
  7. }  
public class Test {
	public static void main(String[] args) {
		int i=0;
		i=i++;
		System.out.println(i);
	}
}



大家說這裏爲什麼會輸出0呢,這裏我們從字節碼的角度解釋
通過反編命令javap –c classname來獲得字節碼

Java代碼 複製代碼
  1. Compiled from "Test.java"  
  2. public class Test extends java.lang.Object{   
  3. public Test();   
  4.   Code:   
  5.    0:   aload_0   
  6.    1:   invokespecial   #8//Method java/lang/Object."<init>":()V   
  7.    4:   return  
  8.   
  9. public static void main(java.lang.String[]);   
  10.   Code:   
  11.    0:   iconst_0   
  12.    1:   istore_1   
  13.    2:   iload_1   
  14.    3:   iinc    11  
  15.    6:   istore_1   
  16.    7:   getstatic   #16//Field java/lang/System.out:Ljava/io/PrintStream;   
  17.    10:  iload_1   
  18.    11:  invokevirtual   #22//Method java/io/PrintStream.println:(I)V   
  19.    14:  return  
  20.   
  21. }  
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方法裏面的,附上解釋

Java代碼 複製代碼
  1. 0:  iconst_0        這裏是聲明瞭一個常量0,並壓入堆棧   
  2. 1:  istore_1        將常量0彈出賦值給本地便量表index爲1位置的變量(也就是i)   
  3.     以上完成了int i=0  
  4. 2:  iload_1     把本地變量表index爲1的位置的值(也就是i的值)壓入堆棧   
  5.   
  6. 3:  iinc    11        i的值加1也就是將本地便量表index爲1的位置的值加1,但是相應的堆棧裏面沒有加   
  7. 上完成了i++   
  8. 6:  istore_1        將堆棧裏面的0彈出賦值給i(也就是0賦值給了i)   
  9. 裏完成了i=i++  
   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類型返回。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章