- 最近編程時候,遇見了一個問題,當時怎麼都想不通。最後,誰知是多態。所以要靜心研究下多態。
多態的定義
- 多態一般分爲兩種:
- 編譯時的多態:
- 方法的重載
- 運行時的多態:
- 繼承
- 實現接口
- 編譯時的多態:
- 我們所說的多態一般是運行時的多態。
- 要使用多態,在聲明對象時就應該遵循一條法則:聲明的總是父類類型或接口類型,創建的是實際類型。
- 面向對象的三大特徵: 封裝 繼承 多態
- 多態通過分離 做什麼 和 怎麼做 ,從另外的一個角度將接口和實現分離開來。
- 封裝通過合併特徵和行爲來創建新的數據類型。“實現隱藏”則是通過將細節“私有化”把接口和實現分離開。
- 多態的作用是消除類型之間的耦合關係。
- 多態也叫作動態綁定,後期綁定,運行時綁定。
- 首先是一個多態的例子:
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中出了 final 和 static 方法之外,所有的方法都是後期綁定。
- 聲明爲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中,所有的轉型都會得到檢查。這種運行期間對類型進行檢查的行爲稱作“運行時的類型識別”。