Java 多态之“绑定”
在剛開始學習 JavaSE 時,學習多態時對下面幾段程序輸出的結果感覺到迷惑
public class Sub extends Super{public int num = 1; public int getNum(){return num;}public static void main(String[] args) {Super sup = new Sub();System.out.println("sup.num = "+sup.num); //調用父類的屬性System.out.println("sup.getNum() = "+sup.getNum()); //調用子類的方法} }class Super{public int num = 0;public int getNum(){return num;} } 輸出:
sup.num = 0
sup.getNum() = 1
當將方法定義為靜態時輸出又會是什么樣的呢?
public class Sub extends Super{public static int num = 1;public static int getNum(){return num;}public static void main(String[] args) {Super sup = new Sub();System.out.println("sup.num = "+sup.num); //調用父類的屬性System.out.println("sup.getNum() = "+sup.getNum()); //調用父類的方法} }class Super{public static int num = 0;public static int getNum(){return num;} } 輸出:
sup.num = 0
sup.getNum() = 0
如果將方法定義成私有的又會是什么結果呢?
public class Sub extends Super{private void f(){System.out.println("Sub f run........... ");}}class Super{private void f(){System.out.println("Super f run........... ");}public static void main(String[] args) {Super sup = new Sub();sup.f(); //調用父類的方法} } 輸出:
Super f run………..
在當時剛開始學 Java 時,當時的疑惑主要有:
- 為什么第一個程序在向上轉型的過程中調用的是父類的字段,以及子類的方法
- 為什么第二個程序在向上轉型的過程中調用的是父類的字段,以及父類的方法
后來在網上搜了一下知道了“綁定”這個概念,但是也只是很淺顯的知道了有這個概念,最近在看《Java 編程思想》對“綁定”這個概念有了更深刻的認識,現在分享出來供大家參考:
綁定:將一個方法調用同一個方法主題關聯起來被稱為綁定。
- 靜態綁定:若程序執行前綁定 (如果有的話,由編譯器和鏈接程序實現),叫做前期綁定。
- 后期綁定:在運行時根據對象的類型進行綁定。后期綁定也叫做動態綁定或運行時綁定。
在 Java 中除了 static 方法和 final 方法 (private 方法屬于 final 方法) 之外,其他的方法都屬于后期綁定。為什么要將一個方聲明為 final 的呢?因為這樣做可以有效“關閉”動態綁定,或者說告訴編譯器不需要對其進行動態綁定。
Java 中所有的方法都是通過動態綁定實現多態。因此子類在向上轉型時調用的是子類的方法 (此方法時非靜態的),這就可以解釋為什么在第一段程序中調用 getNum() 方法輸出的是子類的方法。
只有普通的方法調用是多態的,因此如果你要訪問某個作用域或靜態方法,這個訪問將在編譯器進行解析。正如第一個程序中輸出的是父類的成員,以及第二個程序中將方法修飾成 static 時輸出的就是父類的成員和父類的方法了 (失去了多態的特性)。
靜態方法是與類,而并非與單個對象相關聯的。如果你并不想將方法定義成靜態的還想要得到 Super.num,必須顯示的指明 super.num,就像下面這樣:
public class Sub extends Super{public int num = 1;public int getNum(){return super.num;}public static void main(String[] args) {Super sup = new Sub();System.out.println("sup.getNum() = "+sup.getNum());} }class Super{public int num = 0;public int getNum(){return num;} } 輸出:
sup.getNum() = 0
對于第三段程序將方法定義成 private 的,我們期望是輸出的是子類的方法運行,但是結果卻出我們所料,這是動態綁定的缺陷所造成的結果,以及包括上面的域與靜態方法都是它的缺陷造成的 (也就是說這兩種缺陷是前期綁定)。
結論
只有非 private 方法才可以被覆蓋,但是還是要注意覆蓋 private 方法的現象,這時編譯器雖然不會報錯,但是它不會按照我們期望的那樣執行。確切的說,在導出類中,對于基類中的 private 方法最好不要采取一樣的名字。
在這里再強調一下這句話:在 Java 中除了 static 方法和 final 方法 (private 方法屬于 final 方法) 之外,其他的方法都屬于后期綁定。
其實在實踐過程中,這種混淆的問題通常不會發生,因為你通常會將所有的域都設成 private 的,因此不能直接訪問它們,其副作用是只能通過方法獲取它們。另外,你幾乎不可能將基類中的域和導出類的域賦予相同的名字。
參考資料
《Java 編程思想》Bruce Eckel 著 陳昊鵬 譯
總結
以上是生活随笔為你收集整理的Java 多态之“绑定”的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Java对象的访问定位
- 下一篇: 刘老师,部队士兵转业是不是考公共基础知识