java编程思想---第八章(多态)

多态, 抽象, 继承, 这三种基本类型特征。

多态通过分离做什么,怎么做, 就是 讲接口和接口的具体实现分离开来。

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 报错
    }
}

分类在转为子类的时候,会发生异常



猜你喜欢

转载自blog.csdn.net/skycanf/article/details/80096547
今日推荐