本文探討Java執行時(動態)繫結機制。

author: ZJ 2007-11-7
執行時繫結也叫動態繫結,它是一種呼叫物件方法的機制。Java呼叫物件方法時,一般採用執行時繫結機制。
1Java的方法呼叫過程

  • 編譯器檢視物件的宣告型別和方法名(物件變數的宣告型別)。通過宣告型別找到方法列表。
  • 編譯器檢視呼叫方法時提供的引數型別。
  • 如果方法是privatestaticfinal或者構造器,編譯器就可以確定呼叫那個方法。這是靜態繫結。
  • 如果不是上述情況,就要使用執行時(動態)繫結。在程式執行時,採用動態繫結意味著:虛擬機器將呼叫物件實際型別所限定的方法。
2.執行時(動態)繫結的過程

  • 虛擬機器提取物件的實際型別的方法表;
  • 虛擬機器搜尋方法簽名;
  • 呼叫方法。
注意,這裡說的是物件的實際型別。即在多型的情況下,虛擬機器可以找到所執行物件的真正型別。

3.在向上轉型情況下的動態繫結示例

public class Father {

    public void method() {

       System.out.println(父類方法,物件型別: + this.getClass());

    }

}

 

public class Son extends Father {

    public static void main(String[] args) {

       Father sample = new Son();//向上轉型

       sample.method();

    }

}
結果1
父類方法,物件型別:class samples.Son

 

這個結果沒有疑問,宣告的是父類的引用(控制程式碼),但準確的呼叫了子類的物件,呼叫method,在子類中沒有該方法,所以去父類中尋找到並呼叫之。
現在修改子類,重寫(override)method方法。
public class Son extends Father {

    public void method() {

       System.out.println(子類方法,物件型別: + this.getClass());

    }

   

    public static void main(String[] args) {

       Father sample = new Son();//向上轉型

       sample.method();

    }

}
結果2
子類方法,物件型別:class samples.Son

 

這個結果也是意料之中的。呼叫method時,在子類中尋找到了該方法,所以直接呼叫之。

4.靜態繫結成員變數

在處理Java類中的成員變數時,並不是採用執行時繫結,而是一般意義上的靜態繫結。所以在向上轉型的情況下,物件的方法可以“找到”子類,而物件的屬性還是父類的屬性。
現在再進一步變化,在父類和子類中同時定義和賦值同名的成員變數name,並試圖輸出該變數的值。
public class Father {

    protected String name=父親屬性;

   

    public void method() {

       System.out.println(父類方法,物件型別: + this.getClass());

    }

}

 

public class Son extends Father {

    protected String name=兒子屬性;

   

    public void method() {

       System.out.println(子類方法,物件型別: + this.getClass());

    }

   

    public static void main(String[] args) {

       Father sample = new Son();//向上轉型

       System.out.println(呼叫的成員:+sample.name);

    }

}
結果3
呼叫的成員:父親屬性

 

這個結果表明,子類的物件(由父類的引用handle)呼叫到的是父類的成員變數。所以必須明確,執行時(動態)繫結針對的範疇只是物件的方法。
現在試圖呼叫子類的成員變數name,該怎麼做?最簡單的辦法是將該成員變數封裝成方法getter形式。
public class Father {

    protected String name = 父親屬性;

 

    public String getName() {

       return name;

    }

 

    public void method() {

       System.out.println(父類方法,物件型別: + this.getClass());

    }

}

 

public class Son extends Father {

    protected String name=兒子屬性;

   

    public String getName() {

       return name;

    }

   

    public void method() {

       System.out.println(子類方法,物件型別: + this.getClass());

    }

   

    public static void main(String[] args) {

       Father sample = new Son();//向上轉型

       System.out.println(呼叫的成員:+sample.getName());

    }

}
結果4
呼叫的成員:兒子屬性