第八章:多态(下)

来源:互联网 发布:python在线编译器 编辑:程序博客网 时间:2024/05/24 07:38

8.3.2 继承与清理

如果一个子类对象依赖于其他对象,销毁的顺序应该与初始化顺序相反.对于字段则意味着和声明的顺序相反(因为字段初始化顺序是和声明顺序相同的).对于基类应该先对子类进行清理.因为子类的清理可能调用父类的某个方法.

8.3.3 构造器内部的多态方法的行为

在一般方法的内部,动态绑定的调用是在运行时才决定的,因为对象无法知道他是属于方法所在的那个类,还是属于那个类的导出类(子类).
在构造器内部调用一个动态绑定的方法,就要用到这个方法被覆盖后的定义,这有可能会出现问题.因为被覆盖的方法在对象被完全构造之前就会被调用.

public class DuoTai3 {    public static void main(String args[]) {        new Son(2);    }}class Father {Father() {    System.out.println("Father before draw()...");    draw();    System.out.println("Father after draw()...");}    void draw() {        System.out.println("Father.draw()...");    }}class Son extends Father {    private int i = 10;    Son(int i) {        this.i = i;        System.out.println("Son.i,i = " + i);    }    @Override    void draw() {        System.out.println("Son.draw(),i = " + i);    }}

运行结果:
Father before draw()…
Son.draw(),i = 0
Father after draw()…
Son.i,i = 2

注意:子类的构造方法会隐式调用父类的无参构造方法
那么为什么会出现上述结果呢?
原因在于初始化顺序:

  1. 在任何事物发生之前,将分配给对象的存储空间初始化成二进制的0
  2. 如上篇博客所讲那样调用构造器.此时调用子类的draw()方法,由于步骤1的缘故,此时的i的值是默认值为0
  3. 按照声明的顺序调用成员的初始化方法.
  4. 调用子类的构造方法主体

因此在编写构造器时,避免调用其他方法.在构造器内唯一可以安全调用的是基类中的final方法(private方法自动属于final方法)
你可能在想可不可以调用static方法呢?答案是可以的,因为static方法不存在被子类覆盖的问题.

8.4 协变返回类型

Java SE5中添加了协变返回类型,它表示在导出类中的被覆盖的方法可以返回基类方法的返回类型的某种导出类型.

8.5 用继承进行设计

继承组合 实在设计代码时会用到的两种方式,当我们要使用现成的类来创建新类时,就会考虑使用这两种方式其中的一种.那么如何选择呢?
最好的方式是首选 组合

  1. 组合不会强制我们的程序设计进入继承的层次结构中
  2. 组合更加灵活,因为他可以动态选择类型(因此也就选择了行为).相反,继承需要在编译是就需要知道确切的类型.

    public class DuoTai4 {    public static void main(String args[]) {        Stage stage = new Stage();        stage.play();        stage.change();        stage.play();    }}class Actor {    public void act() {    }}class HappyActor extends Actor {    @Override    public void act() {        System.out.println("HappyActor");    }}class SadActor extends Actor {    @Override    public void act() {        System.out.println("SadActor");    }}class Stage {    private Actor actor = new HappyActor();    public void change() {        actor = new SadActor();    }    public void play() {        actor.act();    }}

    上述代码将Actor定义为了成员变量,也就是采用了组合的方式,可以动态的更换actor的值.与此相反,我们不能在运行期间决定继承不同的对象,因为他要求在编译期间就完全确定下来.
    通用的准则:”用继承表达行为间的差异,并用字段表达状态上的变化

8.5.2 向下转型与运行时类型识别

首先向上转型是安全的,因为向上转型是子类—>父类,子类中肯定有父类的方法 属性,所以安全
向下转型必须先进行向上转型然后再进行向下转型
多态意味着不同的形式

0 0
原创粉丝点击