Thinking in java 第8章 多态 笔记+习题

本文是关于《Thinking in java》第8章多态的笔记,涵盖转机、构造器与多态的概念,讨论了Java中后期绑定的工作原理、构造器调用顺序以及多态在类继承中的应用。通过一系列习题,深入探讨了多态在不同场景下的表现和潜在问题。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Thinking in java 第8章 多态

学习目录


8.2 转机

1. Java中除了static方法和final方法之外,其他所有方法都是后期绑定(在运行时根据对象的类型进行绑定)。因此一般我们不必判断是否应该进行后期绑定,它会自动发生。

2. final可以关闭后期绑定以调用生成更有效的代码,提高性能。但对整体没太大影响。

3. 正是由于后期绑定,编译器能够正确调用向上转型后导出类重写的方法。

4. 缺陷:

  • 导出类不能覆盖私有方法,会被当成一个新的方法(入口不能缩小)。
  • 如果一个方法是静态方法,则不具有多态性。

 

8.3 构造器与多态

1. 构造器的调用顺序:(练习12,先初始化完成员对象再是构造器内的其他内容)

  1. 在其他任何事物发生之前,将分配给对象的存储空间初始化成二进制的零
  2. 调用基类构造器
  3. 按照声明的顺序调用成员的初始化方法
  4. 调用导出类的构造器主体

2. 若要清理时,一定要先清理导出类再清理基类(super.dispose()),因为导出类的清理可能会用到基类中的某些方法。

3. 若在基类的构造函数中调用某个方法,且该方法被导出类重写,这时向上转型时会发生错误,因为导出类还没被初始化好其中的方法就被使用了。(例P163)

 


习题

练习1:创建一个Cycle类,它具有子类Unicycle,Bicycle和TriCycle.演示每一个类型的实例都可以经由ride()向上转型为Cycle。

package Chapter8;

public class E1 {
    public static void main(String[] args) {
        Cycle[] cycles = new Cycle[] {new Cycle(), new Unicycle(), new Bicycle(), new Tricycle()};
        for(Cycle c:cycles) {
            rideBike(c);
        }
    }

    public static void rideBike(Cycle c) {
        c.ride();
    }
}

class Cycle{
    void ride() {
        System.out.println("Cycle ride");
    }
}

class Unicycle extends Cycle{
    @Override
    void ride() {
        System.out.println("Unicycle ride");
    }
}

class Bicycle extends Cycle{
    @Override
    void ride() {
        System.out.println("Bicycle ride");
    }
}

class Tricycle extends Cycle{
    @Override
    void ride() {
        System.out.println("Tricycle ride");
    }
}

/*
Cycle ride
Unicycle ride
Bicycle ride
Tricycle ride
*/

练习2:在几何图形的示例中添加@Override注解。

略。实际如果有自信没错也可以不加,加了等于上个保险。

练习3:在基类Shape.java中添加一个新方法,用于打印一条消息,但导出类中不要覆盖这个方法。请解释发生了什么。现在,在其中一个导出类中覆盖该方法,而在其他的导出类不予覆盖,观察又有什么发生。最后,在所有的导出类中覆盖这个方法。

略。若覆盖则使用导出类自己的覆盖方法,否则用基类。

练习4:向Shape.java中添加一个新的Shape类型,并在main()方法中验证:多态对新类型的作用是否与在旧类型中的一样。

略。同练习1。

练习5:以练习1为基础,在Cycle中添加wheels()方法,它将返回轮子的数量。修改ride()方法,让它调用wheels方法,并验证多态起作用了。

略。练习1改方法名并返回一个常数即可。

练习6和练习7,略。

练习8:修改Music3.java,使其可以像Shapes.java中的方式那样可以随机创建Instrument对象。

package Chapter8;

import java.util.Random;

public class E8 {
    public static void main(String[] args) {
        Instrument[] ins = new Instrument[5];
        for (int i = 0; i < ins.length; i++) {
            ins[i] = randomIns();
        }
        tuneAll(ins);
    }

    public static void tuneAll(Instrument[] ins) {
        for(Instrument i:ins) {
            i.play(Note.MIDDLE_C);
        }
    }

    public static Instrument randomIns() {
        Random r = new Random();
        int temp = r.nextInt(4);
        switch (temp) {
            case 0:
                return new Wind();
            case 1:
                return new Percussion();
            case 2:
                return new Stringed();
            case 3:
                return new Brass();
            default:
                return new Instrument();
        }
    }
}

enum Note{
    MIDDLE_C
}

class Instrument {
    void play(Note n) { System.out.println("Instrument.play() " + n); }
    String what() { return "Instrument"; }
    void adjust() { System.out.println("Adjusting Instrument"); }
}

class Wind extends Instrument {
    void play(Note n) { System.out.println("Wind.play() " + n); }
    String what() { return "Wind"; }
    void adjust() { System.out.println("Adjusting Wind"); }
}

class Percussion extends Instrument {
    void play(Note n) { System.out.println("Percussion.play() " + n); }
    String what() { return "Percussion"; }
    void adjust() { System.out.println("Adjusting Percussion"); }
}

class Stringed extends Instrument {
    void play(Note n) { System.out.println("Stringed.play() " + n); }
    String what() { return "Stringed"; }
    void adjust() { System.out.println("Adjusting Stringed"); }
}

class Brass extends Wind {
    void play(Note n) { System.out.println("Brass.play() " + n); }
    void adjust() { System.out.println("Adjusting Brass"); }
}

/*
Wind.play() MIDDLE_C
Brass.play() MIDDLE_C
Percussion.play() MIDDLE_C
Wind.play() MIDDLE_C
Brass.play() MIDDLE_C
*/

练习9:创建Rodent(啮齿动物):Mnouse(老鼠),Gerbil(鼹鼠),Hamster(大颊鼠),等等这样一个的继承层次结构。在基类中,提供对所有的Rodent都通用的方法,在导出类中,根据特定的Rodent类型覆盖这些方法,以便它们执行不同的行为。创建一个Robent数组,填充不同的Rodent类型,然后调用基类方法,观察发生什么情况。

基本同上。略。

练习10:创建一个包含两个方法的基类。在第一个方法中可以调用第二个方法。然后产生一个继承自该基类的导出类,且覆盖基类中的第二个方法。为该导出类创建一个对象,将他向上转型到基类并调用第一个方法,解释发生的情况。

package Chapter8;

public class E10 {
    public static void main(String[] args) {
        E10A e = new E10B();
        e.func1();
    }
}

class E10A{
    void func1() {
        System.out.println("A func1");
        func2();
    }

    void func2() {
        System.out.println("A func2");
    }
}

class E10B extends E10A{
    @Override
    void func2() {
        System.out.println("B func2");
    }
}

/*
A func1
B func2
*/

练习11:向Sandwich.java中添加Pickle类。

 意义不明。略。

练习12:修改练习9,使其能够演示基类和导出类的初始化顺序。然后向基类和导出类中添加成员对象,并说明构造期间初始化发生的顺序。

package Chapter8;

public class E12 {
    public static void main(String[] args) {
        E12B e = new E12B();
    }
}

class E12A{
    private int i = print(11);
    E12A() {
        System.out.println("AAA");
    }
    int print(int a) {
        System.out.println(a);
        return a;
    }
}

class E12B extends E12A{
    private int i = print(22);
    E12C e = new E12C();
    E12B(){
        System.out.println("BBB");
    }
}

class E12C{
    E12C() {
        System.out.println("CCC");
    }
}

/*
11
AAA
22
CCC
BBB
*/

逐级往上,先初始化完成员对象再是构造器内的其他内容。

练习13:在ReferenceCounting.java中添加一个finalized()方法,用来校验终止条件。

略。

练习14:修改练习12,使其某个成员对象变为具有引用计数的共享对象,并证明它可以正常运行。

即添加一个private static int对象,每次new都使其++,并为每个对象添加一个private final int变量记录id,都用Getter获取值。

练习15:在PolyConstructors.java中添加一个RectangularGlyph,并证明会出现本节所描述的问题。

package Chapter8;

public class E15 {
    public static void main(String[] args) {
        Glyph g = new RectangularGlyph(1,2);
    }
}

class Glyph {
    void draw() {
        System.out.println("Glyph.draw()");
    }
    Glyph() {
        System.out.println("before draw");
        draw();
        System.out.println("after draw");
    }
}

class RectangularGlyph extends Glyph {
    @Override
    void draw() {
        System.out.println("RectangularGlyph.draw(), length = " + length + ", width = " + width);
    }

    private int length, width;

    RectangularGlyph(int a, int b) {
        this.length = a;
        this.width = b;
        System.out.println("RectangularGlyph.RectangularGlyph(), length = " + a + ", width = " + b);
    }
}

/*
before draw
RectangularGlyph.draw(), length = 0, width = 0
after draw
RectangularGlyph.RectangularGlyph(), length = 1, width = 2
*/

练习16:遵循Transmogrify.java这个例子,创建一个Starship类,包含一个AlertStatus引用,此引用可以指示三种不同的状态。纳入一些可以改变这些状态的方法。

package Chapter8;

public class E16 {
    public static void main(String[] args) {
        StarShip ship = new StarShip();
        ship.getAlert().alert();
        for(int i = 1; i <= 3; i++) {
            ship.changeAlert(i);
            ship.getAlert().alert();
        }
    }
}

class StarShip{
    private AlertStatus a;
    StarShip() {
        System.out.println("ship has been built");
        a = new Status1();
    }

    AlertStatus getAlert() {
        return this.a;
    }

    void changeAlert(int i) {
        switch (i) {
            case 2:
                a = new Status2();
                break;
            case 3:
                a = new Status3();
                break;
            default:
                a = new Status1();
        }
    }
}

class AlertStatus {
    void alert() {}
}

class Status1 extends AlertStatus {
    @Override
    void alert() {
        System.out.println("Alert 1");
    }
}

class Status2 extends AlertStatus {
    @Override
    void alert() {
        System.out.println("Alert 2");
    }
}

class Status3 extends AlertStatus {
    @Override
    void alert() {
        System.out.println("Alert 3");
    }
}

/*
ship has been built
Alert 1
Alert 1
Alert 2
Alert 3
*/

练习17:使用练习1中的Cycle的层次结构,在Unicycle 和Bicycle中添加balance方法,而Tricycle中国不添加。创建这三种类型的实例,并将他们向上转型为Cycle数组。在该数组的每一个元素上都尝试调用balance,并观察结果。然后将他们向下转型,再次调用balance(),并观察将所发生什么。

略。没有向下转型时,编译会出错。向下转型后可以正常运行。 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值