跳轉到內容

編譯器構造/Java

來自華夏公益教科書,開放的書籍,為開放的世界

在Java中,有四種方法呼叫,分別是invokestaticinvokespecialinvokevirtualinvokeinterface。顧名思義,第一個用於呼叫靜態方法,其餘用於呼叫例項方法。由於靜態方法不能被重寫,因此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 是超類方法的名稱。

從語義上來說,invokeinterfaceinvokevirtual 並沒有區別,但它可以給編譯器一個關於呼叫的提示。

類方法

[編輯 | 編輯原始碼]

類方法可以用 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 虛擬機器可能會忽略訪問標誌,以相同的方式處理每個欄位。

華夏公益教科書