1. 程式人生 > >Java筆記:組合、繼承

Java筆記:組合、繼承

Java組合、繼承

組合

為達到同時使用幾個類的方法,可以採用組合的方法。就是將幾個類的方法例項化成物件,並變為另一個類的成員。為實現某個功能,可直接在其他區域呼叫此類中的物件的方法(需注意訪問限定符);或在此類的public方法中呼叫其成員的方法,引數由大類方法傳至小類方法。要注意在類開頭宣告成員變數時只是建立在棧中的引用變數,需在外面的大類構造方法中用new小類構造方法建立引用變數指向的存在堆中的實際值,否則會出現空指向的錯誤。注意在建立一個由物件構成的陣列時,例:Car[] cars = new Car[10]; 中,倘若只有這一句,僅僅讓引用變數cars指向10個地址值,而沒有在堆中開闢十個物件的真實值,呼叫Car[0]到Car[9],仍會出現空指向的錯誤。

public class Car {
    private door theDoor;
    private wheel theWheel;

    public Car(){
        theDoor = new door();
        theWheel = new wheel();
    }

    public static void main(String[] args){
        Car theCar = new Car();
        theCar.theDoor.open();
        theCar.theDoor.close();
        theCar.theDoor.down();
        theCar.theDoor.down();
        theCar.theWheel.roll();
    }
}

class window{

    public window() { }

    public void up(){
        System.out.println("window up");
    }

    public void down(){
        System.out.println("window down");
    }
}

class wheel{
    public void roll(){
        System.out.println("wheel roll");
    }
}

class door{
    private window theWindow;

    public door(){
        theWindow = new window();
    }
    public void up(){
        theWindow.up();
    }

    public void down(){
        theWindow.down();
    }

    public void open(){
        System.out.println("open door");
    }

    public void close(){
        System.out.println("close door");
    }
}

通過在Car類中定義其他類型別的成員變數,可以呼叫其方法,實現了組合。

繼承

Java建立一個類時,不是隱式繼承於Object類,就是顯式繼承於指定類,使用extends關鍵字,語法為在子類名後新增 extends 父類名稱。而後子類擁有父類所有的成員變數和方法(private成員和方法除外)。final類不可被繼承。

class goodDoor extends door{

    int height;

    public goodDoor(){
        this.height = super.height + 10;
    }

    public void up(){
        System.out.println("重寫父類up()方法:");
        super.up();
    }
    
    public int getHeight(){
        return this.height;
    }
}

緊接上文的door類,同時為door類添加了height成員變數,並在構造方法中賦值10。goodDoor類繼承door類,同時重寫父類up()方法。此處引出方法重寫和super關鍵字的講述:

方法重寫

方法重寫,就是在子類繼承父類時,子類的一個方法名與父類中一個方法名稱、引數列表、返回值相同,子類方法對父類方法進行重新寫入,在呼叫子類物件該方法時,出現的是子類的該方法。 類方法的重寫應滿足下列條件: 1、 子類中該方法的返回值應與父類方法名稱、返回值、引數列表相同。 名稱不同必不為重寫,由於返回值和引數列表足以區分兩個方法,名稱相同,但其他兩項不同則相當於方法過載,此時需遵守過載規則。 2、 子類中重寫方法的訪問許可權不能比基類小,許可權順序為:public > protected > private。 而若在子類中寫入與父類private方法同名的方法,則兩方法間非重寫,沒有任何關係。 3、 父類中final方法不能被子類重寫。 4、 父類abstract方法必須被非抽象類(abstract)子類重寫。

幾個關鍵字

super關鍵字 1、在子類中可使用super關鍵字引用父類中的非private成員變數和方法。當父類中方法被子類重寫時,在子類中使用super.方法名仍可呼叫父類未被重寫的方法。 2、子類要呼叫父類的構造方法super(),super()只能在子類的構造方法中呼叫,且位於首行。即不能在對子類物件初始化後又使用父類的初始化方法。

protected 關鍵字 由protected修飾的變數和方法,對於在同一包內或其子類來說是可以訪問的,而對於其他類來說不能訪問。至此應該放上一張從別處拿來的圖來加以說明幾個訪問修飾符。

在其他區域中呼叫:

goodDoor door1 = new goodDoor();
door1.up();
door1.down();
System.out.print(door1.getHeight());

輸出為: 重寫父類up()方法:window up window down 20

可見重寫方法執行了自己方法體內的語句。而列印door1.getHeight()結果為20,說明在物件初始化時先依照父類的方法初始化出height = 10; 後執行子類的構造方法子類的height等於父類的height + 10,得到最終20的結果。此處引出含有繼承關係的幾個類的初始化順序:

含有繼承關係的類的初始化順序

用下面的程式碼檢視類中各部分初始化順序:

class test0{
    test0(){
        System.out.println("父類靜態成員變數");
    }
}

class test1{
    test1(){
        System.out.println("父類普通成員變數");
    }
}

class test2{
    test2(){
        System.out.println("子類靜態成員變數");
    }
}

class test3{
    test3(){
        System.out.println("子類普通成員變數");
    }
}

class father{
    static test0 t0 = new test0();
    test1 t1 = new test1();

    static{
        System.out.println("父類靜態塊");
    }

    {
        System.out.println("父類例項塊");
    }

    father(){
        System.out.println("父類構造方法");
    }
}

class son extends father{
    static test2 t2 = new test2();
    test3 t3 = new test3();

    static {
        System.out.println("子類靜態塊");
    }

    {
        System.out.println("子類例項塊");
    }

    son(){
        System.out.println("子類構造方法");
    }
}

在其他地方son theSon = new son(); 來檢視執行結果: 父類靜態成員變數 父類靜態塊 子類靜態成員變數 子類靜態塊 父類普通成員變數 父類例項塊 父類構造方法 子類普通成員變數 子類例項塊 子類構造方法

可以看出,歸類所有的靜態部分被先初始化,而後是非靜態部分,在靜態部分和非靜態部分都是先進行完父類全部有的,在進行子類全部有的。倘若上述程式碼中father類還有父類,則在靜態部分和非靜態部分的兩個最前面加上father父類的初始化。

向上轉型

即父類引用指向子類物件,在定義變數時語法為:父類名 物件 = new 子類名(引數); 傳統的類繼承圖是從上向下畫的,父類在上,其子類依次在下,因而由父類引用指向子類物件在類繼承圖上是向上移動的,稱為向上轉型。因為子類繼承了父類,子類中至少含有父類的所有內容,或後續增加子類成員、方法使得子類內容多於父類。向上轉型中引用所指向的內容只可能減少而不可能增加因而不會出現空指向的情況,因此向上轉型可隱式發生。

向下轉型也可發生於呼叫引數為父類物件的方法時,向其中傳入子類物件,因為父類所含有的方法不比子類多,所以使用了父類物件的方法,對子類物件也適用。例如:

class animal{
    animal(){}

    protected void shout(){
        System.out.println("shout");
    }
}

class dog extends animal{

    public void shout(){
        System.out.println("dog bark");
    }

}

此處dog類繼承了animal類且重寫了shout方法,在另一個類中寫一個方法:

public static void shout(animal theAnimal){
    theAnimal.shout();
}

此處可見引數為父類animal型別,在該類main方法new兩個物件,再呼叫。

dog theDog = new dog();
animal theDog2 = new dog();
shout(theDog);
shout(theDog2);

結果為 dog bark dog bark 不論引用是父類還是子類,傳入shout()方法後都呼叫了dog類中的重寫shout方法。若兩物件直接呼叫shout()方法:

theDog.shout();
theDog2.shout();

結果仍為: dog bark dog bark 此時為dog類新增一個獨有的方法:

public void eat(){
    System.out.println("dog eat");
}

兩物件呼叫:

theDog.eat();
((dog) theDog2).eat();
//!theDog2.eat();

向上轉型的物件無法使用子類中後續新增的方法,必須強制轉型才可使用該類方法。