第8章 多態

  • 最近編程時候,遇見了一個問題,當時怎麼都想不通。最後,誰知是多態。所以要靜心研究下多態。

多態的定義

  • 多態一般分爲兩種:
    • 編譯時的多態:
      • 方法的重載
    • 運行時的多態:
      • 繼承
      • 實現接口
  • 我們所說的多態一般是運行時的多態。
  • 要使用多態,在聲明對象時就應該遵循一條法則:聲明的總是父類類型或接口類型,創建的是實際類型。
  • 面向對象的三大特徵: 封裝 繼承 多態
  • 多態通過分離 做什麼 和 怎麼做 ,從另外的一個角度將接口和實現分離開來。
  • 封裝通過合併特徵和行爲來創建新的數據類型。“實現隱藏”則是通過將細節“私有化”把接口和實現分離開。
  • 多態的作用是消除類型之間的耦合關係。
  • 多態也叫作動態綁定,後期綁定,運行時綁定。
  • 首先是一個多態的例子:
class Instrument {
  public void play(Note n) {
    print("Instrument.play()");
  }
}

public class Wind extends Instrument {
  public void play(Note n) {
    System.out.println("Wind.play() " + n);
  }
}

public class Music {
  public static void tune(Instrument i) {
    i.play(Note.MIDDLE_C);
  }
  public static void main(String[] args) {
    Wind flute = new Wind();
    tune(flute); // Upcasting
  }
}
// Output:
//Wind.play() MIDDLE_C

方法調用綁定

  • 將一個方法同一個方法主體關聯起來叫做 綁定。如果程序執行前進行綁定的話,叫做前期綁定。(這一般是面向過程語言的綁定過程)

  • 後期綁定:運行時根據對象的類型進行綁定。

  • 後期綁定在不同的語言中事先有所不同,但是想象一下就會得知,不知怎樣必須在對象中安置某種類型信息。
  • java中出了 finalstatic 方法之外,所有的方法都是後期綁定。
  • 聲明爲final方法的好處:防止其他人覆蓋該方法。但是更重要的一點是:這樣做可以有效地關閉動態綁定。

多態的“缺陷”

  • private方法被自動的認爲是final方法,對於導出類是屏蔽的。
public class PrivateOverride {
  private void f() { print("private f()"); }
  public static void main(String[] args) {
    PrivateOverride po = new Derived();
    po.f();
  }
}

class Derived extends PrivateOverride {
  public void f() { print("public f()"); }
} /* Output:
private f()
  • 只有普通的方法調用可以是多態的。域(變量)和靜態的方法也不具有多態性質。
class Super {
  public int field = 0;
  public int getField() { return field; }
}

class Sub extends Super {
  public int field = 1;
  public int getField() { return field; }
  public int getSuperField() { return super.field; }
}

public class FieldAccess {
  public static void main(String[] args) {
    Super sup = new Sub(); // Upcast
    System.out.println("sup.field = " + sup.field +
      ", sup.getField() = " + sup.getField());
    Sub sub = new Sub();
    System.out.println("sub.field = " +
      sub.field + ", sub.getField() = " +
      sub.getField() +
      ", sub.getSuperField() = " +
      sub.getSuperField());
  }
} /* Output:
sup.field = 0, sup.getField() = 1
sub.field = 1, sub.getField() = 1, sub.getSuperField() = 0
*///:~

構造器和多態

  • 對於導出類的構造器,在第一行必須調用 基類 的構造器,否則編譯器會報錯。當然,如果基類有無參的構造器,則可以不用顯式的調用,因爲編譯器會幫助我們隱式調用。
    • 首先將分配給對象的空間初始化爲二進制的0
    • 按聲明順序調用成員的初始化方法
    • 然後調用該類的構造器
  • 根據以上規則,當我們手動寫dispose()方法的時候,我們銷燬的順序應該是和初始化的順序是相反的,因爲這樣可以避免某個子類對象依賴其他的對象。
  • 如果遇到類之間有共享變量的情況,那麼銷燬的順序就變得複雜了,一般採用計數器的方法,記錄着引用的個數。
  • 在構造器內唯一安全調用的那些方法是final方法,也適用於private方法,因爲他們自動屬於final方法。

協變返回類型

  • 協變返回類型就是如果方法要求返回一個對象,則我們可以返回這個對象的子類。

向下轉型

  • 向上轉型我們會丟失具體的類型信息
  • 在java中,所有的轉型都會得到檢查。這種運行期間對類型進行檢查的行爲稱作“運行時的類型識別”。

參考

發佈了76 篇原創文章 · 獲贊 2 · 訪問量 2萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章