04、對象的建立和通路指令
Java 是一門面向對象的程式設計語言,那麼 Java 虛拟機是如何從位元組碼層面進行支援的呢?
1)建立指令
數組也是一種對象,但它建立的位元組碼指令和普通的對象不同。建立數組的指令有三種:
newarray:建立基本資料類型的數組
anewarray:建立引用類型的數組
multianewarray:建立多元數組
普通對象的建立指令隻有一個,就是 new,它會接收一個操作數,指向常量池中的一個索引,表示要建立的類型。
舉例來說。
public void newObject() {
String name = new String("沉默王二");
File file = new File("無愁河的浪蕩漢子.book");
int [] ages = {};
}
通過 jclasslib 看一下 newObject() 方法的位元組碼指令。

new #13 <java/lang/String>,建立一個 String 對象。
new #15 <java/io/File>,建立一個 File 對象。
newarray 10 (int),建立一個 int 類型的數組。
2)字段通路指令
字段可以分為兩類,一類是成員變量,一類是靜态變量(static 關鍵字修飾的),是以字段通路指令可以分為兩類:
通路靜态變量:getstatic、putstatic。
通路成員變量:getfield、putfield,需要建立對象後才能通路。
public class Writer {
private String name;
static String mark = "作者";
public static void main(String[] args) {
print(mark);
Writer w = new Writer();
print(w.name);
}
public static void print(String arg) {
System.out.println(arg);
}
}
通過 jclasslib 看一下 main() 方法的位元組碼指令。
getstatic #2 <com/itwanger/jvm/Writer.mark>,通路靜态變量 mark
getfield #6 <com/itwanger/jvm/Writer.name>,通路成員變量 name
05、方法調用和傳回指令
方法調用指令有 5 個,分别用于不同的場景:
invokevirtual:用于調用對象的成員方法,根據對象的實際類型進行分派,支援多态。
invokeinterface:用于調用接口方法,會在運作時搜尋由特定對象實作的接口方法進行調用。
invokespecial:用于調用一些需要特殊處理的方法,包括構造方法、私有方法和父類方法。
invokestatic:用于調用靜态方法。
invokedynamic:用于在運作時動态解析出調用點限定符所引用的方法,并執行。
public class InvokeExamples {
private void run() {
List ls = new ArrayList();
ls.add("難頂");
ArrayList als = new ArrayList();
als.add("學不動了");
}
public static void print() {
System.out.println("invokestatic");
}
public static void main(String[] args) {
print();
InvokeExamples invoke = new InvokeExamples();
invoke.run();
}
}
我們用 javap -c InvokeExamples.class 來反編譯一下。
Compiled from "InvokeExamples.java"
public class com.itwanger.jvm.InvokeExamples {
public com.itwanger.jvm.InvokeExamples();
Code:
0: aload_0
1: invokespecial #1 // Method java/lang/Object."<init>":()V
4: return
private void run();
Code:
0: new #2 // class java/util/ArrayList
3: dup
4: invokespecial #3 // Method java/util/ArrayList."<init>":()V
7: astore_1
8: aload_1
9: ldc #4 // String 難頂
11: invokeinterface #5, 2 // InterfaceMethod java/util/List.add:(Ljava/lang/Object;)Z
16: pop
17: new #2 // class java/util/ArrayList
20: dup
21: invokespecial #3 // Method java/util/ArrayList."<init>":()V
24: astore_2
25: aload_2
26: ldc #6 // String 學不動了
28: invokevirtual #7 // Method java/util/ArrayList.add:(Ljava/lang/Object;)Z
31: pop
32: return
public static void print();
Code:
0: getstatic #8 // Field java/lang/System.out:Ljava/io/PrintStream;
3: ldc #9 // String invokestatic
5: invokevirtual #10 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
8: return
public static void main(java.lang.String[]);
Code:
0: invokestatic #11 // Method print:()V
3: new #12 // class com/itwanger/jvm/InvokeExamples
6: dup
7: invokespecial #13 // Method "<init>":()V
10: astore_1
11: aload_1
12: invokevirtual #14 // Method run:()V
15: return
}
InvokeExamples 類有 4 個方法,包括預設的構造方法在内。
1)InvokeExamples() 構造方法中
預設的構造方法内部會調用超類 Object 的初始化構造方法:
`invokespecial #1 // Method java/lang/Object."<init>":()V`
2)成員方法 run() 中
invokeinterface #5, 2 // InterfaceMethod java/util/List.add:(Ljava/lang/Object;)Z
由于 ls 變量的引用類型為接口 List,是以 ls.add() 調用的是 invokeinterface 指令,等運作時再确定是不是接口 List 的實作對象 ArrayList 的 add() 方法。
invokevirtual #7 // Method java/util/ArrayList.add:(Ljava/lang/Object;)Z
由于 als 變量的引用類型已經确定為 ArrayList,是以 als.add() 方法調用的是 invokevirtual 指令。
3)main() 方法中
invokestatic #11 // Method print:()V
print() 方法是靜态的,是以調用的是 invokestatic 指令。
方法傳回指令根據方法的傳回值類型進行區分,常見的傳回指令見下圖。