1. 程式人生 > 實用技巧 >第七章 複用類

第七章 複用類

複用類的關鍵在於使用類但是不破壞現有程式碼。

7.1 組合語法

1、toString方法

每一個非基本型別的類都應該有一個這樣的方法。

當編譯器需要一個String而自己只有一個物件的時候,這個方法就會得到呼叫。

2、引用初始化的四個位置

(1)在定義物件的地方。

private String s = "Hello World!"

(2)在類的構造器中。

public Bath(){
		s1 = "a";
		s2 = "b";
}

(3)惰性初始化。

public String toString(){
		if(s3 == NULL){
				s3 = "c";
		}
}

(4)使用例項初始化。

{
		s4 = "d";
}

7.2 繼承語法

繼承在Java中總是存在的,要麼是顯式繼承了自己寫的類,要麼是隱式繼承了標準根類Object。

訪問許可權在繼承中很重要,如果對類不加public,那麼其預設是包繼承許可權。

而對域和方法,推薦把所有的域設定為private,方法設定為public。

子類會繼承父類的public、protected成員,同一包內會繼承friendly成員,不可能繼承private成員。

子類會繼承規則允許的方法,但是同時子類也可以自己重寫這些方法,或者新增一些方法。

7.2.1 初始化基類

1、繼承不只是複製基類的介面。

當建立了一個匯出類的物件時,該物件包含了一個基類的子物件。

基類在匯出類構造器可以訪問它之前,就已經完成了初始化。

class Art{
    Art(){
        System.out.println("Art Constructor has been used.");
    }
}

class Drawing extends Art{
    Drawing(){
        System.out.println("Drawing Constructor has been used.");
    }
}


public class Cartoon extends Drawing{
    Cartoon(){
        System.out.println("Cartoon Constructor has been used.");
    }

    public static void main(String[] args){
        Cartoon test = new Cartoon();
    }
}

輸出結果為:

2、帶引數的構造器。

我們必須使用super關鍵字來顯示地呼叫基類的關鍵字。

這也是我們在子類構造器中首先要做的。

7.3 代理

1、其實我覺得這一部分可以參見《大話設計模式》的代理部分來看,還是挺簡單易懂的。

婷婷、戴笠、左嘉莊三個人之間的故事。左嘉莊和戴笠兩個人做的事情是一模一樣的,也就是他們有相同的介面,但二者又不是繼承關係。

2、代理是繼承和組合的中庸之道。

3、代理的用處:遠端代理、虛擬代理、安全代理、智慧指引。

7.4 結合使用組合和繼承

繼承要注意初始化哦。

7.4.1 確保正確處理

1、許多情況下,清理不是問題。

2、我其實不想在這塊費很多功夫,因為我對Java的需求現在是能實現我的功能,垃圾處理顯得不是很重要,或許說過段時間自己在看JVM虛擬機器的時候這段才是重點。

3、繼承類的垃圾處理和利用構造器的初始化的順序是不同的。

應該是先對子類使用垃圾處理,然後再對其中包含的基類的物件垃圾進行處理。

7.4.2 名稱處理

1、在Java中其實不用擔心名字重複這個事情,Java已經幫我們做的很好了。

2、過載與重寫。

3、這個註解是來幫助我們一定來進行重寫的,如果我們過載的話編譯器會報錯的。

@Override

7.5 在組合和繼承之間選擇

1、組合和繼承都允許在類中放置子物件,組合是顯式地這樣做,繼承則是隱式地這樣做。

2、組合通常用於想在新類中使用其子物件而非使用介面的情況。

3、繼承則主要用於為某個類開發特定版本。

7.6 protected

我們還是推薦把基類中的域全部設定為private。

7.7 向上轉型

1、繼承更多的是用來表示邏輯關係。

7.7.1 什麼叫向上轉型

從傳統的類繼承圖來看,超類在上,子類在下。因此成為向上轉型。

向上轉型是從一個較專用型別轉型為較通用型別,所以是比較安全的,

向上轉型一般要閹割方法。

7.7.2 再論組合與繼承

實際生產生活中我們是不推薦過多使用繼承的。

繼承的使用原則:必須要使用到向上轉型。如果不是必須的話,那麼就最好不要繼承。

7.8 final關鍵字

final通常的含義:不允許改變。

final使用的三種情況:資料、方法、類。

7.8.1 final資料

1、永不改變的編譯時常量(只要帶上final就可以)。

static final 名稱要大寫。

2、不希望這個資料改變可以用final。

3、空白final:無論什麼情況,編譯器都令空白final在使用前被初始化。

4、final引數:當基本型別的引數被宣告final時:你可以讀取資料,但是你沒辦法改變資料。

7.8.2 final方法

1、第一層原因:鎖定方法,不允許任何繼承類修改它的含義。

不是不讓用,只是不讓改。

2、二、效率問題:這是因為很早之前的問題了,現在編譯器和虛擬機器能幫助我們解決掉這些問題。

3、類中所有的private方法都被隱式地指明瞭是final方法。

7.8.3 final類

1、含義:不希望與任何子類繼承他。

2、final類中所有的方法都是隱式地宣告為final的。

7.8.4 對於final的一些忠告

主要是說要不要將方法標記為final啊。

標記final的意思是不希望有人來繼承我的方法,但其實有的人繼承了這些方法能幹很多事情的。

7.9 初始化及類的載入

每個類的編譯程式碼都存在於它自己的獨立檔案之中,該檔案只有在需要使用程式程式碼的時候才會被載入。

7.9.1 繼承與初始化

執行時,先訪問main函式。

載入器開始啟動並找出該類的編譯程式碼。

載入時,由於extends知道有一個基類,於是載入基類。

直到所有的基類載入完畢。

很重要的一點是static方法是在載入類的時候就被執行了。

所有的類載入完畢。開始建立物件。

物件中所有的基本型別會被設定為預設值。

然後呼叫構造器。(注意,基類構造器會在構造器中優先執行。)

class Insect{
    private int i = 9;
    protected int j;
    Insect(){
        System.out.println("i = " + i + "    j = " + j);
        j = 39;
    }

    private static int x1 = printInit("static Insetc.x1 initialized");

    static int printInit(String s){
        System.out.println(s);
        return 47;
    }
}


public class Beetle extends Insect{
    private int k = printInit("Beetle.k initialized");

    public Beetle(){
        System.out.println("k = " + k);
        System.out.println("j = " + j);
    }

    private static int x2 = printInit("static Beetle.x2 initialized");

    public static void main(String[] args){
        System.out.println("Beetle Constructor");
        Beetle b = new Beetle();
    }

}

Result:

7.10 總結

1、繼承和組合都能從現有型別生成新的型別。

組合複用類,繼承複用介面。

2、優先選擇組合。設計向上轉型的時候選擇繼承。

3、在設計一個系統的時候,我們的目標應該是找到或者建立某些類,每個類都有具體用途,都能承擔功能。

4、把專案當做有機體去培養,去孕育,付出時間和精力來不斷進行優化。

不要只是說能做出來就行。