導讀:複用類包含組合和繼承兩種方式,多用組合,少用繼承
組合:新的類由現有對象所組成,包含現有類的對象,這種方式僅是複用了現有程序代碼中的功能,而非它的形式。
繼承:按照父類的類型來創建新類(子類)。子類無需改變父類的形式,只需要擴展功能方法就可以。編譯器可以處理好子類和父類的關係。
繼承是面向對象程序設計的基石之一。
1.組合
- 將先有類的對象置於新類中就可實現組合。
- 基本類型可以直接定義,非基本類型的類必須將引用置於新類中,並且,基本類型可以默認初始化,但非基本類型的對象引用會被初始化位null
- 試圖調用一個爲空的引用編譯器會拋出運行時的異常,在初始化和清理中提到過。
- 惰性初始化:在正要使用對象之前初始化。生成的對象不值得及不必每次都生成對象的情況下,這種方式可以減少額外的負擔。
public class Test01 { //基本類型 private String s; //使用現有類 private ArrayList list; }
2.toString()
- 每一個非基本類型的對象都會有這麼個方法,作用是當編譯器只想獲取一個String時而你卻只有一個對象時,這個方法便會被自動調用
如:System.out.println(new Test01());
3.繼承
- 繼承是所有OOP語言和Java不可或缺的組成部分。
- 當創建一個類時,總是在繼承,因此,除非已明確指出要從其他類中繼承,否則就隱式的從java標準根類Object進行繼承,就像void隱式返回null一樣。
- 集成過程:聲明“子類和父類相似”,使用extends關鍵字表示
- 運行問題:main是一個程序的入口,即使一個類不是public類,如果運行的Run的是這個類的話,它的main方法也會被執行,不管這個類的訪問權限修飾詞是什麼
- 子類可以使用父類中的被protected public修飾的方法和參數,如果在一個包中,那麼也可以直接調用。但是private是不可以的,它只屬於那個類,所有其他類都不可能訪問
- 子類就像一個與父類相同接口(方法)的新類,可能子類還會擴展一些自己的方法和域。
4.super關鍵字
- 這種情況發生在子類重寫父類的方法的過程中,在子類中重寫的父類方法中調用父類的這個方法,如果不加super關鍵字,你將會得到一個java.lang.StackOverflowError這樣的異常
如果你想在重寫的父類方法中調用父類的這個方法,就使用super關鍵字吧。調用其他父類方法倒沒有問題。 - 不加super,對象調用的時候將會產生遞歸。
- super將調用父類中的方法。
5.初始化基類(父類)
- 子類在初始化的時候,父類在幹嘛??
- 在構造器中調用父類的構造器來進行初始化。java會自動在子類的構造器中插入對父類構造器的調用,這是隱式的。 sd
class Art{ Art(){System.out.println("Art constructor");} } class Drawing extends Art{ Drawing(){System.out.println("Drawing constructor");} } public class Cartoon extends Drawing { Cartoon(){System.out.println("Cartoon constructor");} public static void main(String[] args) { Cartoon x = new Cartoon(); } } /** * 輸出可以看出其父類和子類初始化的順序,先初始化最頂層,最後到子類 * Art constructor * Drawing constructor * Cartoon constructor */
6.帶參數的構造器
- 上一例中所表示的都是默認的無參構造器,編譯器會自動調用它們,但如果一個類中定義了有參數的構造器,
那麼默認的構造器就會消失,被這個有參構造器代替,那其子類怎麼調用基類構造器,對基類初始化。 - 解決辦法就是super關鍵字,如下代碼
class Art { Art(int i) { System.out.println("Art constructor" + i); } } class Drawing extends Art { Drawing() { // 調用父類的有參構造器 super(12); System.out.println("Drawing constructor"); } } public class Cartoon extends Drawing { Cartoon() { System.out.println("Cartoon constructor"); } public static void main(String[] args) { Cartoon x = new Cartoon(); } } /** * Art constructor12 * Drawing constructor * Cartoon constructor */
繼承中構造器的執行順序
- 調用基類中的構造器,這個步驟會遞歸下去,知道最底層的到處類,(根類)。
- 然後從根類構造器開始,執行構造器中的代碼
- 最後調用導出類構造器的主題
- 當進行繼承時,我們已經知道了基類的一切,並且可以訪問基類中的任何聲明爲public和protected的成員,而這一切的前提必須保證基類得到了正確的初始化,基類中的成員都是有效的。
也就是說,必須首先調用基類構造器。這樣就可以保證基類中供我們訪問的成員都已得到了初始化了。
繼承過程中清理順序
- 繼承中的清理順序正好與初始化相反,原因是子類清理過程中可能會調用父類中的某些方法,所以需要父類中的構建仍起作用而不應該過早的銷燬他們。
7.代理
- 這是一個特殊的解決問題的方法,就是在使用類和實現類之間插入一箇中間件,這個中間件有實現類的對象,並有調用實現類方法的方法,實用類調用中間件就可調用實現類。
如下代碼真正的實現類public class SpaceShipControls { void up(){} void down(){} void left(){} void right(){} }
中間件public class SpaceShipDelegation { private SpaceShipControls controls = new SpaceShipControls(); public void up() { controls.up(); } public void down() { controls.down(); } public void left() { controls.left(); } public void right() { controls.right(); } }
- 使用代理可以比繼承獲得更多的控制能力,因爲我們可以選擇只提供成員對象(實現類)中的方法的某個子集,而非全部。
8.結合使用組合和繼承
- 在很多時候,我們編寫的代碼都是結合使用組合和繼承的。
- 編譯器可以強制你去初始化基類,並且要求你在構造器起始處就要這麼做,但你的成員對象的初始化編譯器並不會監聽,所以要小心運行時異常
9.確保正確的清理
- 內存方面的清理我們是無法干預的,這是垃圾回收器的工作,回收無用對象並釋放內存。
- 但有時類可能要在器生命週期內執行一些必要的清理工作,你並不知道垃圾回收器什麼時候開始工作,
所以想要清理一些東西,必須顯示的編寫一個特殊的方法來完成這項工作,並確保客戶端程序員知道必
須要調用這個清理方法。public class Shape { private ArrayList mList = new ArrayList(); /** * 清理方法 */ public void dispose(){ if (mList.size() != 0) { //生命週期內的清理工作 mList.clear(); } } }
- 除了內存回收外,不能依賴垃圾回收器去做任何事情。如果需要清理,最好編寫自己的清理方法,但不要使用finalize(),gc就更不能調用啦。
10.@Override註解
- 當你重寫父類中的方法是可以使用這個註解
- 這個關鍵字可以方式你在不想重載時而意外的進行了重載
重寫父類中的方法並進行重載,但是因爲有關鍵字存在會報錯,public class parent { void setText(){ System.out.println("父類"); } }
public class Test01 extends parent{ @Override void setText(int i) {//這兒編譯器會報錯 super.setText(); } }
- 這個關鍵字表示你重寫了父類中的方法,你不可以重載這個方法。
11.組合和繼承選擇
- 使用組合通常是想在新類中使用現有類的功能而非它的接口(方法)的情況。
- 而繼承可能需要父類的方法形式,需要重寫等情況。
- “is-a”(是一個)的關係是用繼承來表達的,而“has-a”(有一個)的關係是用組合來表達的
- 慎用繼承,多用組合。
protected關鍵字是爲繼承而誕生的,表示繼承此類,就可以使用此類中被protected關鍵字修飾的方法變量,當然這個關鍵字還有包訪問權限,
當一個類沒有繼承它也沒再同一包中,將不能訪問被這個關鍵字修飾的資源。
12.向上轉型
- 爲新的類提供方法 並不是繼承技術中最重要的方面,最重要的方面是用來表現新類和基類之間的關係【新類是現有類的一種類型】。
- 因爲涉及向上轉型,java會查找子類中重寫的父類方法,如果有重寫就執行子類重寫的方法,沒有,就執行父類的方法,
class Instrument{ static void tune(Instrument i){} } public class Wind extends Instrument{ public static void main(String[] args) { Wind wind = new Wind(); //這種情況就叫做向上轉型 Instrument.tune(wind); //這種就需要強制類型轉換啦, Wind wind2 = (Wind) new Instrument(); //但是你不能將一件兵器強轉成樂器,這是不允許的,下面這句就非常的呵呵 String s = new ArrayList<E>(); } }
- 爲什麼叫做向上型
在繼承圖上是向上移動的,所以叫做向上轉型 - 向上轉型是有一個專有類型向一個普通類型轉換的過程,所以說是安全的,
在向上轉型的過程中,可能會丟失方法(接口),而不是獲取方法,這就是編譯器在
“未曾明確表示轉型”或者“未曾制定特殊標記”的情況下,仍然允許向上轉型 - 如果是向下轉型,就是要獲取未曾有過的方法了,這是就需要特殊的標記了。
總結:
- 生成和使用程序代碼中最有可能採用的方法就是直接將數據和方法封裝進一個類中,也可以使用組合使用現有類來開發新類。但繼承並不常用。
- 慎用繼承,多用組合。