java編程思想---第八章(多態)

多態, 抽象, 繼承, 這三種基本類型特徵。

多態通過分離做什麼,怎麼做, 就是 講接口和接口的具體實現分離開來。

在Java類中使用super來調用父類中的指定操作: super可用於訪問父類中定義的屬性 super可用於調用父類中定義的成員方法 super可用於在子類構造方法中調用父類的構造器 注意: 尤其當子父類出現同名成員時,可以用super進行區分 super的追溯不僅限於直接父類 super和this的用法相像,this代表本類對象的引用,super代表父類的內存空間的標識

1、8.1 向上轉型的由來

先看下代碼演化:

 

class Instrument {
    public void play(Note n) {
        System.out.println("Instrument.play()");
    }
}
public class Wind extends Instrument {
    // Redefine interface method:子類重寫了父類play的方法,後期調用都是子類的實現,除非調用super來調用父類的paly
    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);//Instrument 裏傳遞的參數是它的子類(wind),
    }
    public static void main(String[] args) {
        Wind flute = new Wind();//構造空對象。
        tune(flute); // Upcasting
    }
}
class Brass extends Instrument {
    public void play(Note n) {
        System.out.println("Brass.play() " + n);
    }
}

8.2.1 方法調用綁定

前期綁定:將一個方法調用同一個方法主體關聯起來被稱作綁定,若在程序執行之前進行綁定。

後期綁定:在運行期間 根據對象的類型進行綁定。亦可以稱爲動態綁定或運行時候綁定。

如果一種語言實現後期綁定,就必須具有某種機制,一遍在運行時候判斷對象的類型,也就是說,編譯器一直不知道對象的類型,但是方法調用機制能找到正確的方法體,加以調用。

java代碼中除了static和final (private方法屬於final方法) 之外,其他所有的方法都是後期綁定。

 

public class PrivateOverride {
    private void f() { System.out.println("private f()"); }
    public static void main(String[] args) {
        PrivateOverride po = new Derived();
        po.f();
    }
}

class Derived extends PrivateOverride {
    public void f() { System.out.println("public f()"); }
}

實際結果調用的是父類的f()方法,也就是說 父類的private是不能覆蓋的,

8.2.5域和靜態方法

 

class StaticSuper {
    public static String staticGet() {
        return "Base staticGet()";
    }
    public String dynamicGet() {
        return "Base dynamicGet()";
    }
}

class StaticSub extends StaticSuper {
    public static String staticGet() {
        return "Derived staticGet()";
    }
    public String dynamicGet() {
        return "Derived dynamicGet()";
    }
}

public class StaticPolymorphism {
    public static void main(String[] args) {
        StaticSuper sup = new StaticSub(); // Upcast
        System.out.println(sup.staticGet());//調用父類的靜態方法,
        System.out.println(sup.dynamicGet());//因爲子類重寫了父類方法,所以調用的是子類的方法
    }
}

靜態方法是與類,而非與單個單向相關聯。

8.3 構造器和多態

 

class Meal {
    Meal() { System.out.println("Meal()"); }
}

class Bread {
    Bread() { System.out.println("Bread()"); }
}

class Cheese {
    Cheese() { System.out.println("Cheese()"); }
}

class Lettuce {
    Lettuce() { System.out.println("Lettuce()"); }
}

class Lunch extends Meal {
    Lunch() { System.out.println("Lunch()"); }
}

class PortableLunch extends Lunch {
    PortableLunch() { System.out.println("PortableLunch()");}
}

public class Sandwich extends PortableLunch {
    private Bread b = new Bread();
    private Cheese c = new Cheese();
    private Lettuce l = new Lettuce();
    public Sandwich() { System.out.println("Sandwich()"); }
    public static void main(String[] args) {
        new Sandwich();
    }
}

 

解析:1)優先調用父類構造器,不斷的反覆下去,一直到構造這種層次的根,也就是最底層的類,然後在調用子類。

 

2)按照聲明順序調用成員的初始化方法。

3)最後調用當前類的構造器主體。

這樣達到所有的成員對象都被初始化。

8.3.2繼承和初始化

若對象需要清理的時候,則,子類必須要重寫父類方法,且在重寫的方法重調用父類的dispose方法(super調用),否則父類清理不會發生。銷魂的順序和創建的順序想法,優先銷燬子類的方法,從裏向外擴展。成員變量也是倒推銷燬。

8.3.3構造器內部的多態方法的行爲

 

class Glyph {
    void draw() { System.out.println("Glyph.draw()"); }
    Glyph() { 
        System.out.println("Glyph() before draw()");
        draw();//① 因爲子類重寫了darw ,所以調用子類方法
        System.out.println("Glyph() after draw()");
    }
}

class RoundGlyph extends Glyph {
    private int radius = 1;
    RoundGlyph(int r) {
        radius = r;//③
        System.out.println("RoundGlyph.RoundGlyph(), radius = " + radius);
    }
    void draw() {
        System.out.println("RoundGlyph.draw(), radius = " + radius);//②子類的初始值還未加載,所以第一次radius當前爲0,
    }
}

public class PolyConstructors {
    public static void main(String[] args) {
        new RoundGlyph(5);//優先加載父類構造器
    }
}

 

初始化實際過程:

 

1)在其他事物發生之前,將分配給對象的存儲空間初始化城二進制的零

2)優先調用父類構造器,到根部,

3)按照聲明的順序調用成員的初始化方法。

4)調用導出類的構造器主體

8.5用繼承進行設計

 

class Actor {
    public void act() {}
}

class HappyActor extends Actor {
    public void act() { System.out.println("HappyActor"); }
}

class SadActor extends Actor {
    public void act() { System.out.println("SadActor"); }
}

class Stage {
    private Actor actor = new HappyActor();
    public void change() { actor = new SadActor(); }
    public void performPlay() { actor.act(); }
}

public class Transmogrify {
    public static void main(String[] args) {
        Stage stage = new Stage();
        stage.performPlay();
        stage.change();
        stage.performPlay();
    }
}

第一調用performplay 爲happactor的對象,第二次給改變了,就爲sadactor對象了,

對象引用在運行的時候可以與另一個不同的對象重新綁定起來, 

8.5.2向下轉型和運行時類別判定

 

class Useful {
    public void f() {
        System.out.println("userful   f");
    }
    public void g() {  System.out.println("userful   g");}
}

class MoreUseful extends Useful {
    public void f() {
        System.out.println( "mode    f");
    }
    public void g() {   System.out.println( "mode    g");}
    public void u() {}
    public void v() {}
    public void w() {}
}

public class RTTI {
    public static void main(String[] args) {
        Useful[] x = {
                new Useful(),
                new MoreUseful()
        };
        x[0].f();
        x[1].g();
        // Compile time: method not found in Useful:
        //! x[1].u();
        ((MoreUseful)x[1]).u(); // Downcast/RTTI
        ((MoreUseful)x[0]).u(); // Exception thrown 報錯
    }
}

分類在轉爲子類的時候,會發生異常

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章