編譯器構造/Java
在Java中,有四種方法呼叫,分別是invokestatic、invokespecial、invokevirtual、invokeinterface。顧名思義,第一個用於呼叫靜態方法,其餘用於呼叫例項方法。由於靜態方法不能被重寫,因此invokestatic非常簡單;它本質上與在C中呼叫函式相同。
現在我們來看看例項方法呼叫的機制。考慮以下程式碼。
class A {
public static int f () { return 1; }
private int g_private () { return 2; }
public final int g_final () { return 3; }
public int g_non_final () { return 4; }
public void test (A a) {
a.f (); // static; this is always 1.
a.g_private (); // special; this is always 2.
a.g_final (); // special; this is always 3.
a.g_non_final (); // virtual; this may be 4 or something else.
}
}
class B extends A {
public int g_non_final () { return 6; }
}
class C extends B {
public int g_non_final () { return 7; }
public int foo () { return A.this.g_non_final (); }
}
invokestatic 使用對類名和方法名的引用來呼叫,並從堆疊中彈出引數。表示式A.f (2)被編譯成
iconst_2 // push a constant 2 onto the stack invokestatic A.f // invoke a static method // the return value is at the top of the stack.
在Java中,私有方法不能被重寫。因此,必須根據類來呼叫方法,而與物件的建立方式無關。invokespecial 允許這樣做;該指令與 invokestatic 相同,只是它除了提供的引數之外,還會彈出物件引用。到目前為止,動態繫結尚未使用,在執行時也不需要關於私有方法的繫結資訊。
具體來說,invokespecial 可以用於 (1) 呼叫私有方法或 (2) 呼叫超類的方法(包括超類的建構函式,即 <init>)。要呼叫除 <init> 之外的超類方法,必須像 super.f () 那樣寫,其中 f 是超類方法的名稱。
從語義上來說,invokeinterface 與 invokevirtual 並沒有區別,但它可以給編譯器一個關於呼叫的提示。
類方法可以用 static 修飾符定義。私有類方法可以在同一個物件中,如果它們屬於不同的類。兩個公共類方法不能在同一個物件中;換句話說,類方法不能被重寫。這也意味著 final 修飾符對類方法在語義上沒有意義。
每個欄位都是基於類訪問的。考慮以下內容。
class A {
public int i = 2;
}
class B extends A {
public int i = 3;
}
B b = new B (); A a = b; b.i++; // this would be 3 + 1 = 4 a.i++; // this would be 2 + 1 = 3
換句話說,訪問控制修飾符(無、public、private 和 protected)隻影響類客戶是否可以訪問給定欄位。這意味著 Java 虛擬機器可能會忽略訪問標誌,以相同的方式處理每個欄位。