java编程思想第四版 第8章习题

本文通过一系列练习探讨了多态性的概念及其应用。包括创建不同类型的Cycle子类,并演示如何通过父类引用调用子类方法;添加新方法并观察多态行为;创建Rodent类层次结构来展示多态性等。

第8章 多态

练习1:

创建一个Cycle类,它具有子类Unicycle、Bicycle和Tricycle。演示每一个类型的实例都可以经由ride()方法向上转型为Cycle。
代码

package testPackage;

import static testPackage.MyPrint.*;

public class Cycle {

    public void run() {
        println("Cycle run");
    }

    public static class Unicycle extends Cycle {
        @Override
        public void run() {
            println("Unicycle run");
        }
    }

    public static class Bicycle extends Cycle {
        @Override
        public void run() {
            println("Bicycle run");
        }
    }

    public static class Tricycle extends Cycle {
        @Override
        public void run() {
            println("Tricycle run");
        }
    }

    public static class PersonGo {
        public void ride(Cycle cycle) {
            cycle.run();
        }

        public static void main(String[] args) {
            Unicycle unicycle = new Unicycle();
            Bicycle bicycle = new Bicycle();
            Tricycle tricycle = new Tricycle();
            PersonGo personGo = new PersonGo();
            personGo.ride(unicycle);
            personGo.ride(bicycle);
            personGo.ride(tricycle);
        }
    }
}

输出

Unicycle run
Bicycle run
Tricycle run

练习2:

在几何图形的示例中添加@override注解。
略。编译器的工作。

练习3:

在基类Shapes.java中添加一个新方法,用于打印一条信息,但导出类中不要覆盖这个方法。请解释发生了什么。现在,在其中一个导出类中覆盖该方法,而在其它的导出类中不予覆盖,观察又有发生什么。最后,在所有导出类中覆盖这个方法。
导出类会优先调用覆盖后的方法,其次调用父类方法。

练习4:

向shapes.java中添加一个新的Shape类型,并在main()方法中验证:多态对新类型的作用是否与在旧类型中的一样。
每个导出类和其他导出类父类实现并无区别。

练习5:

以练习1为基础,在Cycle中添加wleels()方法,它将返回轮子的数量。修改ride()方法,让它调用wheels()方法,并验证多态起作用了。
略。导出类覆盖方法返回整形即可。

练习6:

修改Music3.java,使what()方法成为根Object的toString()方法。试用System.out.println打印Instrument对象(不用向上转型)。

testPackage.Cycle$PersonGo@4554617c
打印出包名类名存储地址。

练习7:

向Music3.java添加一个新的类型Instrument,并验证多态性是否作用于所添加的新类型。

骑上我心爱的自行车。
代码

package testPackage;

import java.util.Random;

import static testPackage.MyPrint.*;

public class Cycle {

    public void run() {
        println("Cycle run");
    }

    public static class Unicycle extends Cycle {
        @Override
        public void run() {
            println("Unicycle run");
        }
    }

    public static class Bicycle extends Cycle {
        @Override
        public void run() {
            println("Bicycle run");
        }
    }

    public static class Tricycle extends Cycle {
        @Override
        public void run() {
            println("Tricycle run");
        }
    }

    public static class RandomCycles {
        private final Random random = new Random(47);

        public Cycle next() {
            switch (random.nextInt(3)) {
                default:
                case 0:
                    return new Bicycle();
                case 1:
                    return new Unicycle();
                case 2:
                    return new Tricycle();

            }
        }
    }

    public static class PersonGo {

        static RandomCycles randomCycles = new RandomCycles();

        public void ride(Cycle cycle) {
            cycle.run();
        }

        public static void main(String[] args) {
            Cycle[] cycles = new Cycle[9];
            for (int i = 0; i < cycles.length; i++) {
                cycles[i] = randomCycles.next();
            }
            PersonGo personGo = new PersonGo();
            for (Cycle cycle : cycles) {
                personGo.ride(cycle);
            }

        }
    }
}

输出

Tricycle run
Tricycle run
Unicycle run
Tricycle run
Unicycle run
Tricycle run
Unicycle run
Tricycle run
Bicycle run

练习9:

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

就像上面一样。

练习10:

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

package testPackage;

import java.util.Random;

import static testPackage.MyPrint.*;

public class Cycle {

    public void run() {
        println("Cycle run");
        println(toString());
    }

    @Override
    public String toString() {
        return getClass().getName() + "@" + Integer.toHexString(hashCode());
    }

    public static class Unicycle extends Cycle {
        @Override
        public void run() {
            println("Unicycle run");
            println(toString());
        }


        @Override
        public String toString() {
            return getClass().getName() + "@" + Integer.toHexString(hashCode());
        }
    }

    public static class Bicycle extends Cycle {
        @Override
        public void run() {
            println("Bicycle run");
            println(toString());
        }

        @Override
        public String toString() {
            return getClass().getName() + "@" + Integer.toHexString(hashCode());
        }

    }

    public static class Tricycle extends Cycle {
        @Override
        public void run() {
            println("Tricycle run");
            println(toString());
        }

        @Override
        public String toString() {
            return getClass().getName() + "@" + Integer.toHexString(hashCode());
        }
    }

    public static class RandomCycles {
        private final Random random = new Random(47);

        public Cycle next() {
            switch (random.nextInt(3)) {
                default:
                case 0:
                    return new Bicycle();
                case 1:
                    return new Unicycle();
                case 2:
                    return new Tricycle();

            }
        }
    }

    public static class PersonGo {

        static RandomCycles randomCycles = new RandomCycles();

        public void ride(Cycle cycle) {
            cycle.run();
        }

        public static void main(String[] args) {
            Cycle[] cycles = new Cycle[9];
            for (int i = 0; i < cycles.length; i++) {
                cycles[i] = randomCycles.next();
            }
            PersonGo personGo = new PersonGo();
            for (Cycle cycle : cycles) {
                personGo.ride(cycle);
            }

        }
    }
}

输出

Tricycle run
testPackage.CycleTricycle@74a14482TricycleruntestPackage.CycleTricycle@74a14482 Tricycle run testPackage.CycleTricycle@74a14482TricycleruntestPackage.CycleTricycle@1540e19d
Unicycle run
testPackage.CycleUnicycle@677327b6TricycleruntestPackage.CycleUnicycle@677327b6 Tricycle run testPackage.CycleUnicycle@677327b6TricycleruntestPackage.CycleTricycle@14ae5a5
Unicycle run
testPackage.CycleUnicycle@7f31245aTricycleruntestPackage.CycleUnicycle@7f31245a Tricycle run testPackage.CycleUnicycle@7f31245aTricycleruntestPackage.CycleTricycle@6d6f6e28
Unicycle run
testPackage.CycleUnicycle@135fbaa4TricycleruntestPackage.CycleUnicycle@135fbaa4 Tricycle run testPackage.CycleUnicycle@135fbaa4TricycleruntestPackage.CycleTricycle@45ee12a7
Bicycle run
testPackage.Cycle$Bicycle@330bedb4

在子类中,父类的方法被覆盖了。

练习11:

向Sandwich中添加Pickle类。
略。

练习12:

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

略。先基类,后导出类,先声明时初始化的成员对象后构造器中初始化的成员对象。

练习13:

在ReferenceCounting.java中添加一个finalize()方法,用来校验终止条件(查看第5章)。

代码

package referencePackage;

import static testPackage.MyPrint.*;

public class Composing {
    private Shared shared;
    private static long counter = 0;
    private final long id = counter++;

    public Composing(Shared shared) {
        println("Creating " + this);
        this.shared = shared;
        this.shared.addRef();
    }

    protected void dispose() {
        println("disposing " + this);
        shared.dispose();
    }

    @Override
    public String toString() {
        return "Composing " + id;
    }

    @Override
    protected void finalize() throws Throwable {
        if (shared.getRef() == 0) {
            println("finalized now");
            super.finalize();
        } else {
            println("is still quoted");
        }
    }
}
package referencePackage;

import static testPackage.MyPrint.*;

public class Shared {
    private int refCount = 0;
    private static long counter = 0;
    private final long id = counter++;

    public Shared() {
        println("Creating " + this);
    }

    public void addRef() {
        refCount++;
    }

    public int getRef() {
        return refCount;
    }

    protected void dispose() {
        if (--refCount == 0) {
            println("Disposing " + this);
        }
    }

    @Override
    public String toString() {
        return "Shared" + id;
    }

    @Override
    protected void finalize() throws Throwable {
        if (refCount == 0) {
            println("finalized now");
            super.finalize();
        } else {
            println("is still quoted");
        }
    }
}
package referencePackage;

import static testPackage.MyPrint.*;

public class ReferenceCounting {
    public static void main(String[] args) {
        Shared shared = new Shared();
        Composing[] composings = {new Composing(shared), new Composing(shared), new Composing(shared),
                new Composing(shared), new Composing(shared)};
        for (Composing c : composings) {
            c.dispose();
        }
        System.gc();
    }

}

输出

Creating Shared0
Creating Composing 0
Creating Composing 1
Creating Composing 2
Creating Composing 3
Creating Composing 4
disposing Composing 0
disposing Composing 1
disposing Composing 2
disposing Composing 3
disposing Composing 4
Disposing Shared0

练习14:

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

代码

package rodent;
import static testPackage.MyPrint.*;

public class Rodent {

    public Rodent(TotalNum totalNum){
        totalNum.addTotal();
    }

    public void predation() {
        println("Rodent eat.");
    }
}
package rodent;
import static testPackage.MyPrint.*;

public class Mouse extends Rodent{
    public Mouse(TotalNum totalNum) {
        super(totalNum);
    }

    @Override
    public void predation() {
        println("Mouse eat");
    }
}
package rodent;

import static testPackage.MyPrint.*;

public class Hamster extends Rodent {
    public Hamster(TotalNum totalNum) {
        super(totalNum);
    }

    @Override
    public void predation() {
        println("Hamster eat.");
    }
}
package rodent;

import static testPackage.MyPrint.*;

public class Gerbil extends Rodent {

    public Gerbil(TotalNum totalNum) {
        super(totalNum);
    }

    @Override
    public void predation() {
        println("Gerbil eat.");
    }
}

输出

Mouse eat
Gerbil eat.
Hamster eat.
3

练习15:

在PolyConstructor.java中添加一个RectangularGlyph,并证明会出现本节所描述的问题。
代码

package draw;

import static testPackage.MyPrint.*;

public class Glyph {

    public Glyph() {
        println("Glyph 1");
        draw();
        println("Glyph 2");
    }

    public void draw() {
        println("Glyph draw");
    }


    public static class RectangleGlyph extends Glyph {
        public RectangleGlyph() {

        }

        @Override
        public void draw() {
            println("RectangleGlyph draw");
        }
    }

    public static class PolyConstructor {
        public static void main(String[] args) {
            new RectangleGlyph();
        }
    }
}

输出

Glyph 1
RectangleGlyph draw
Glyph 2

练习16:

遵循Transmogrify.java这个例子,创建一个Starship类,包含一个AlerSatus引用,此引用可以指示三种不同的装了。纳入一些可以改变这些状态的方法。
代码

package draw;

import sun.management.Sensor;

import static testPackage.MyPrint.*;

public class Do16 {

    public static void main(String[] args) {
        StarShip starShip = new StarShip(new Status());
        starShip.status.alert();
        for (int i = 0; i < 3; i++) {
            starShip.changeStatus(i);
            starShip.status.alert();
        }
    }

    public static class StarShip {
        private Status status = new Status();

        public StarShip(Status status) {
            this.status = status;
        }

        public Status getStatus() {
            return status;
        }

        public void changeStatus(int i) {
            switch (i) {
                default:
                    status = new Status();
                case 1:
                    status = new FirstStatus();
                case 2:
                    status = new SecondStatus();
                case 3:
                    status = new ThirdStatus();

            }
        }
    }

    public static class Status {
        public void alert() {
            println("status alert");
        }
    }

    public static class FirstStatus extends Status {
        @Override
        public void alert() {
            println("First alert");
        }
    }

    public static class SecondStatus extends Status {
        @Override
        public void alert() {
            println("Second alert");
        }
    }

    public static class ThirdStatus extends Status {
        @Override
        public void alert() {
            println("Third alert");
        }
    }
}

输出

status alert
Third alert
Third alert
Third alert

练习17:

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

略。向上转型编译报错,向下转型有该方法可以调用,没有不可调用。

写在前面的话 引言 1. 前提 2. Java的学习 3. 目标 4. 联机文档 5. 节 6. 练习 7. 多媒体 8. 源代码 9. 编码样式 10. Java版本 11. 课程和培训 12. 错误 13. 封面设计 14. 致谢 第1 对象入门 1.1 抽象的进步 1.2 对象的接口 1.3 实现方案的隐藏 1.4 方案的重复使用 1.5 继承:重新使用接口 1.5.1 改善基础 1.5.2 等价和似关系 1.6 多形对象的互换使用 1.6.1 动态绑定 1.6.2 抽象的基础和接口 1.7 对象的创建和存在时间 1.7.1 集合与继承器 1.7.2 单根结构 1.7.3 集合库与方便使用集合 1.7.4 清除时的困境:由谁负责清除? 1.8 违例控制:解决错误 1.9 多线程 1.10 永久性 1.11 Java和因特网 1.11.1 什么是Web? 1.11.2 客户端编程 1.11.3 服务器端编程 1.11.4 一个独立的领域:应用程序 1.12 分析和设计 1.12.1 不要迷失 1.12.2 阶段0:拟出一个计划 1.12.3 阶段1:要制作什么? 1.12.4 阶段2:开始构建? 1.12.5 阶段3:正式创建 1.12.6 阶段4:校订 1.12.7 计划的回报 1.13 Java还是C++? 第2 一切都是对象 2.1 用句柄操纵对象 2.2 必须创建所有对象 2.2.1 保存在什么地方 2.2.2 特殊情况:主型 2.2.3 Java中的数组 2.3 绝对不要清除对象 2.3.1 作用域 2.3.2 对象的作用域 2.4 新建数据型: 2.4.1 字段和方法 2.5 方法、自变量和返回值 2.5.1 自变量列表 2.6 构建Java程序 2.6.1 名字的可见性 2.6.2 使用其他组件 2.6.3 static关键字 2.7 我们的第一个Java程序 2.8 注释和嵌入文档 2.8.1 注释文档 2.8.2 具体语法 2.8.3 嵌入 2.8.4 @see:引用其他 2.8.5 文档标记 2.8.6 变量文档标记 2.8.7 方法文档标记 2.8.8 文档示例 2.9 编码样式 2.10 总结 2.11 练习 第3 控制程序流程 3.1 使用Java运算符 3.1.1 优先级 3.1.2 赋值 3.1.3 算术运算符 3.1.4 自动递增和递减 3.1.5 关系运算符 3.1.6 逻辑运算符 3.1.7 按位运算符 3.1.8 移位运算符 3.1.9 三元if-else运算符 3.1.10 逗号运算符 3.1.11 字串运算符 3.1.12 运算符常规操作规则 3.1.13 造型运算符 3.1.14 Java没有“sizeof” 3.1.15 复习计算顺序 3.1.16 运算符总结 3.2 执行控制 3.2.1 真和假 3.2.3 反复 3.2.6 中断和继续 3.2.7 切换 3.3 总结 3.4 练习 第4 初始化和清除 4.1 由构建器保证初始化 4.2 方法过载 4.2.1 区分过载方法 4.2.2 主型的过载 4.2.3 返回值过载 4.2.4 默认构建器 4.2.5 this关键字 4.3 清除:收尾和垃圾收集 4.3.1 finalize()用途何在 4.3.2 必须执行清除 4.4 成员初始化 4.4.1 规定初始化 4.4.2 构建器初始化 4.5 数组初始化 4.5.1 多维数组 4.6 总结 4.7 练习 第5 隐藏实施过程 5.1 包:库单元 5.1.1 创建独一无二的包名 5.1.2 自定义工具库 5.1.3 利用导入改变行为 5.1.4 包的停用 5.2 Java访问指示符 5.2.1 “友好的” 5.2.2 public:接口访问 5.2.3 private:不能接触 5.2.4 protected:“友好的一种” 5.3 接口与实现 5.4 访问 5.5 总结 5.6 练习 第6 再生 6.1 合成的语法 6.2 继承的语法 6.2.1 初始化基础 6.3 合成与继承的结合 6.3.1 确保正确的清除 6.3.2 名字的隐藏 6.4 到底选择合成还是继承 6.6 递增开发 6.7 上溯造型 6.7.1 何谓“上溯造型”? 6.8 final关键字 6.8.1 final数据 6.8.2 final方法 6.8.3 final 6.8.4 final的注意事项 6.9 初始化和装载 6.9.1 继承初始化 6.10 总结 6.11 练习 第7 多形性 7.1 上溯造型 7.1.1 为什么要上溯造型 7.2 深入理解 7.2.1 方法调用的绑定 7.2.2 产生正确的行为 7.2.3 扩展性 7.3 覆盖与过载 7.4 抽象
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值