因爲繼承的存在, 咱們能夠將子類的對象引用當作基類的一個對象, 即將子類對象向上轉型爲基類對象.java
class A { public void show() { System.out.println("A show()"); } } class B extends A{ public void show() { System.out.println("B show()"); } } public class C { public static void func(A a) { a.show(); } public static void main(String[] args) { B b = new B(); // B show() func(b); } }
綁定ide
將一個方法調用同一個方法主體關聯起來被稱做綁定, 在程序執行前進行綁定稱爲前期綁定.函數
當咱們編寫上例中的代碼:this
public static void func(Cycle c) { c.ride(); }
咱們如何肯定方法ride所對應的對象c是什麼類型?spa
這就引起了動態綁定的概念. 即在靜態編譯階段, 咱們實際上並不知道c的類型是什麼. 只有等到運行階段, 咱們經過某種方法, 得到此方法的類簽名, 才能正確的調用此方法.code
覆蓋引起動態綁定對象
考慮上例中: 只有子類覆蓋了基類的方法, 纔會引起多態. 在動態運行階段, a.show()被執行時候, 其方法簽名若爲B對象, 則它事先查看A中是否有show方法, 若是有則進行向下轉型, 使用實際的B對象調用show.繼承
若是使用private/final阻止多態的產生, 那程序則一般不會按咱們預期的進行執行:遞歸
class A { private void f() { System.out.println("A f()"); } public static void main(String[] args) { A a = new B(); // A f() a.f(); } } public class B extends A{ // @Override public void f() { System.out.println("B f()"); } }
若是咱們要強制執行多態, 則最好使用@Override進行強制覆蓋.編譯
構造器的調用順序
構造器的調用順序必然是先調用基類構造器, 而後遞歸下去調用子類構造器.
構造器的主要任務是: 檢查對象是否被正確構造. 因此只有父類構造器正確的調用, 纔可確保子類的構造器正確的調用.
繼承與清理
在繼承狀況下, 若是須要手動編寫清理函數, 那麼子類的清理函數必須調用父類的清理函數.
對象的銷燬順序應該和初始化順序相反. 考慮順序定義兩個對象A,B, 那麼在B中是能夠引用對象A的. 正確的銷燬步驟是調用B的清理函數, 而後在調用A的清理函數.
若是是先調用A的清理函數, 那麼在調用B的任何函數時(包括銷燬函數), 在B中的A對象是無效的, 則致使異常的發生.
class A { public void dispose() { System.out.println("A dispose"); } } public class B extends A{ private A a = new A(); public void dispose() { System.out.println("B dispose."); a.dispose(); super.dispose(); } public static void main(String[] args) { B b = new B(); b.dispose(); } }
存在一種特殊的狀況, 即一個對象被多個對象所共享. 那麼須要經過計數來判斷何時須要清理對象:
class Shared { private int refcount = 0; private static long counter = 0; private final long id = counter++; public Shared() { System.out.println("Creating " + this); } public void addRef() { refcount++; } public void dispose() { if (--refcount == 0) { System.out.println("disposing " + this); } } public String toString() { return "Shared " + id; } } class Composing { private Shared shared; private static long counter = 0; private final long id = counter++; public Composing(Shared shared) { System.out.println("Creating " + this); this.shared = shared; this.shared.addRef(); } protected void dispose() { System.out.println("disposing " + this); shared.dispose(); } public String toString() { return "Composing " + id; } } public class ReferenceCounting { public static void main(String[] args) { Shared shared = new Shared(); Composing[] composing = { new Composing(shared), new Composing(shared), new Composing(shared), new Composing(shared), new Composing(shared) }; for (Composing c: composing) { c.dispose(); } } }
構造器內部的多態方法的行爲
假設class A extends B, 那麼在A中使用super來引用B. 但super的類型其實是A的, 它向上轉型爲B, 經過強制類型轉換(B) super.
這能夠解釋構造器中的多態行爲:
class A { A() { System.out.println("A constructor."); show(); } public void show() { System.out.println("A show"); } } public class B extends A{ B() { System.out.println("B constructor."); } public void show() { System.out.println("B show"); } public static void main(String[] args) { // A constructor. // B show // B constructor. B b = new B(); } }