綁定,指的是方法的呼叫與其所在的類別關聯起來。
綁定可分為靜態綁定和動態綁定。
在分析靜態綁定和動態綁定之前需要知道的幾個概念:
#編譯期:編譯過程是將Java 來源文件編譯成字節碼(.class文件,JVM 可執行程式碼)的過程,在這個過程中Java是不與記憶體打交道的,在這個過程中編譯器會進行語法的分析,如果語法不正確就會報錯。
運行期:運行過程是指JVM(Java虛擬機)裝載字節碼檔案並解釋執行,在這個過程才是真正的創建內存,執行Java程式。
Java 的方法呼叫過程如下:
編輯器查看物件的宣告類型和方法名。取得所有可能被呼叫的候選方法,因為在方法重載的情況。例如:方法一為 print(String str)、方法二為 print(int )。
編譯器查看呼叫方法的入參類型。從候選方法中挑選匹配的方法。例如入參為 “hello”,則挑選 print(String str)。
若方法是 private、static、final 修飾或建構函數,編譯器可以決定要呼叫哪個方法。這是靜態綁定。
如果不是上述情況,就要使用執行階段(動態)綁定。
靜態綁定,又稱為前期綁定,編譯時綁定。表示編譯期進行的綁定,即程式運行前方法已被綁定。
只有被final,static,private 修飾的方法、成員變數、建構方法是靜態綁定:
##解釋 | |
---|---|
被其修飾的方法可以被繼承,但不能被重寫;子類別物件可以調用,但是調用的是父類別中定義的那個方法;間接表示將方法宣告為final 可以避免重寫,關閉動態綁定。 | |
被其修飾地方法隱含地包含了 final 關鍵字。由於它對外是不可見的,所以不能被繼承,重寫;只能透過類別本身的物件來調用,因此在方法運行之前就可以明確物件。 | |
靜態方法是依賴類別而依賴物件的。它可以被子類別繼承(實質是被子類別隱藏),但是不能被子類別重寫。當子類別物件向上轉型為父類別物件時,不論子類別中有沒有定義這個靜態方法,該物件都會使用父類別中的靜態方法。因此這裡說靜態方法可以被隱藏。 | |
預設 Java 對屬性採用靜態綁定,這樣在編譯期就能發現程式錯誤,能夠提供效率。 | |
建構方法不能被繼承的,子類別繼承父類別時預設要先呼叫父類別的建構方法(無論顯示或隱含)。因此在程式運行之前就可以知道構造方法術語哪個物件。 |
// 父类class Parent{ // 变量 String name="Parent"; // 静态方法 static void print(){ System.out.println("Parent print"); } // 私有方法 private void say(){ System.out.println("Parent say"); } // 终态方法 final void look(){ System.out.println("Parent look"); } }// 子类class Son extends Parent{ String name ="Son"; static void print(){ System.out.println("Son print"); } // 编译错误,无法重写父类的 final方法 final void look(){}; }public class Test{ public static void main(String[] args) { // 发生向上转型 Parent p = new Son(); // 输出 Parent System.out.println(p.name); // 输出 Parent print p.print(); // 编译错误,对外不可见 p.say(); } }
動態綁定的過程:
class A { int x = 5; } class B extends A { int x = 6; } class Parent { public A getValue() { System.out.print("Parent Method "); return new A(); } } class Son extends Parent { public B getValue() { System.out.print("Son Method "); return new B(); } }public class Test { public static void main(String[] args) { // 向上转型 Parent p = new Son(); // 输出结果:Son Method 5 // 注意:是 5 不是 6 ! System.out.println(p.getValue().x); } }
觀察輸出分析如下:
class Parent { String name = "Parent " + this.getClass().getName(); } class Son extends Parent { String name = "Son" + this.getClass().getName(); }public class Test { public static void main(String[] args) { // 向上转型 Parent p = new Son(); // 输出:Parent Son System.out.println(p.name); } }
觀察輸出結果分析如下: