第8章 多态 - 《Java编程思想》读书笔记

第8章 多态

8.2 转机

8.2.5 缺陷:域与静态方法

下文中用属性代替

  • 属性没有动态绑定,不存在多态
class Super{
    public int field = 0;
    public int getField(){
        return field;
    }
}

class Sub extends Super{
    public int field = 1;
    public int getField(){
        return field;
    }
    public int getSuperField(){
        return super.field;
    }
}

public class FieldAccess{
    public static void main(String[] args){
        Super sup = new Sub();
        System.out.println("sup.field = " + sup.field + ", sup.getField() = " + sup.getField());
        Sub sub = new Sub();
        System.out.println("sub.field = " + sub.field + ", sub.getField() = " + sub.getField() + ", sub.getSuperField() = ", sub.getSuperField());
    }
    /* 输出:
    sup.field = 0, sup.getField() = 1
    sub.field = 1, sub.getField() = 1, sub.getSuperField() = 0
    */
}
  • 可以看到,Super.field和Sub.field是在编译时就绑定好的,所以直接使用属性时,是什么类型的引用,就是什么类型的属性

  • 但在使用getField方法时,是动态绑定的,绑定的是什么对象的方法,就会使用什么对象的属性

  • 若要在重写的方法中使用基类的属性,就需要用Super关键字

  • 同样地,静态方法与类关联,也不具有多态性

8.3 构造器和多态

8.3.1 构造器的调用顺序

  1. 调用基类构造器
  2. 按声明顺序调用成员的初始化方法
  3. 调用导出类构造器的主体

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

  • 初始化的实际过程是:
    1. 在其他任何事物发生之前,将分配给对象的存储空间初始化成二进制的0
    2. 调用基类构造器。如果基类构造器中调用在子类中重写的方法,因为子类构造器还未调用,子类的属性值均为默认
    3. 按照声明的顺序调用成员的初始化方法
    4. 调用导出类的构造器主体
  • 因此,编写构造器时,我们应该用尽可能简单的方法使对象进入正常状态,如果可以的话,避免调用其他方法

8.4 协变返回类型

  • Java SE5中添加了协变返回类型,在导出类中重写的方法可以返回基类方法的返回类型的某种导出类型
class Grain{}
class Wheat extends Grain{}
class Mill{
    Grain process(){}
}
class WheatMill extends Mill{
    Wheat process(){} //重写时返回Grain的子类Wheat
}

8.5 用继承进行设计

  • 状态模式:通过组合的方式,引用在运行时可以与另一个不同的对象重新绑定,从而获得动态灵活性
class Actor{}
class HappyActor extends Actor{}
class SadActor extends Actor{}
class Stage{
    private Actor actor = new HappyActor();
    public void change(){
        actor = new SadActor();
    }
}
  • 用继承表达行为间的差异,并用属性表达状态上的变化
posted @ 2021-03-07 15:53  一天到晚睡觉的鱼  阅读(88)  评论(0)    收藏  举报