Java編程思想: 多態

再論向上轉型

因爲繼承的存在, 咱們能夠將子類的對象引用當作基類的一個對象, 即將子類對象向上轉型爲基類對象.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();
  }
}
相關文章
相關標籤/搜索