java的“一處編譯、多處執行”,得益于它的位元組碼和JVM。位元組碼就是java編譯後的能被JVM讀懂的類彙編語言。
1.舉個例子:
package jvm;
public class A {
}
上面是一個最簡單的類,編譯該類:
javac A.java
生成對應的A.class檔案,反編譯該class:
javap A.class
Compiled from "A.java"
public class jvm.A {
public jvm.A();
}
反編譯後,可以看到預設的構造函數jvm.A()。
如果要看到位元組碼,可以使用如下的指令:
javap -c A.class
Compiled from "A.java"
public class jvm.A {
public jvm.A();
Code:
0: aload_0
1: invokespecial #1 // Method java/lang/Object."<init>":()V
4: return
}
簡單解釋一下:
aload_0 表示将第一個變量(引用類型)壓入堆棧,對于非靜态方法,第一個引用變量是this,即将this指針壓棧。aload_0的16進制代碼為0x2a
invokespecial 表示調用超類構造方法,其十六進制碼為0xb7
return 表示目前方法傳回void,其十六進制碼為0xb1
2.更複雜些的例子
package jvm;
public class A {
public int add(int a, int b) {
return a + b;
}
}
更複雜些的類A,編譯再反編譯後,可以看到如下的位元組碼:
javap -c A.class
Compiled from "A.java"
public class jvm.A {
public jvm.A();
Code:
0: aload_0
1: invokespecial #1 // Method java/lang/Object."<init>":()V
4: return
public int add(int, int);
Code:
0: iload_1
1: iload_2
2: iadd
3: ireturn
}
預設的構造函數之外,增加了新的方法int add(int, int),它的位元組碼解釋如下:
iload_1 将第二個變量(int類型)壓棧
iload_2 将第三個變量(int類型)壓棧
iadd 将棧頂兩int型數值相加并将結果壓入棧頂
ireturn 傳回int類型
注意,之是以是iload_1而不是iload_0是因為目前是非靜态方法,第一個變量預設為this。如果上述add方法換為靜态方法,則反編譯後,位元組碼如下:
javap -c A.class
Compiled from "A.java"
public class jvm.A {
public jvm.A();
Code:
0: aload_0
1: invokespecial #1 // Method java/lang/Object."<init>":()V
4: return
public static int add(int, int);
Code:
0: iload_0
1: iload_1
2: iadd
3: ireturn
}
後續文章還會接着本文的例子聊下.class檔案的結構。