thinking in java test chapter8多态(1)~(12)

练习(1):创建一个Cycle类,它具有子类Unicycle,Bycycle,Tricycle.演示每一个类型的实例都可以经由ride()方法向上转型为Cycle.
向上转型就是允许将多种从同一基类的导出类看成同一类型。
多态方法调用就是允许一种类型表现出与其他相似类型之间的区别,只要他们是从同一基类导出而来的。这种区别由各个导出类型方法的具体不同实现而表现出来的,虽然这些方法都是由基类调用的。

public class Test1 {
    public static void main(String[] args){
        Unicycle unicycle = new Unicycle("Unicycle");
        Bicycle bicycle = new Bicycle("Bicycle");
        Tricycle tricycle = new Tricycle("Tricycle");
        Cycle.ride(unicycle);
        Cycle.ride(bicycle);
        Cycle.ride(tricycle);
    }
}
class Cycle{
    private String name;
    public Cycle(String str){
        name = str;
    }
    public static void ride(Cycle c){
        System.out.println(c.name + "is riding");
    }
}
class Unicycle extends Cycle{
    private String name;
    public Unicycle(String str) {
        super(str);
        name = str;
    }
}
class Bicycle extends Cycle{
    private String name;
    public Bicycle(String str) {
        super(str);
        name = str;
    }
}
class Tricycle extends Cycle {
    private String name;
    public Tricycle(String str) {
        super(str);
        name = str;
    }
}

输出:
Unicycleis riding
Bicycleis riding
Tricycleis riding
在以上示例中,三种子类能被视作Cycle传入到方法ride()中就是向上转型。
但向上转型只是看成,而不是强制转换,所以最后方法调用的结果不同,这就是多态。
多态又称之为动态绑定。什么是动态绑定?
与动态绑定相反的是静态绑定。c语言所有方法都是默认静态绑定。静态绑定也称为前期绑定,就是在程序运行前就绑定完成。也就是说,代码写了什么样,就是什么样。
而动态绑定是直到运行时才去决定该方法调用该绑定哪个实体。
java中的所有static和final方法都是静态绑定,其他的所有方法都是动态绑定。

练习(2):在几何图形示例中添加@Override注解。
练习(3):在基类Shape.java中添加一个新方法,用于打印一条消息,但导出类中不要覆盖这个方法。请解释发生了什么。现在,在其中一个导出类中覆盖该方法,而在其他的导出类中不予覆盖,观察又有什么发生。最后,在所有的导出类中覆盖这个方法。
练习(4):向Shapes.java中添加一个新的Shape类型,并在main()方法中验证:多态对新类型的作用是否与在旧类型中的一样。
以上三个练习在一份示例中完成。

public class Test234 {
    private static RandonShapeGenerator gen = new RandonShapeGenerator();
    public static void main(String[] args){
        Shape[] shapes = new Shape[9];
        for (int i = 0; i < shapes.length; i++) {
            shapes[i] = gen.next();
        }
        for (Shape s :
                shapes) {
            s.draw();
            s.newMethod();//每个子类都调用了一次添加的新方法,
            // 因为子类继承父类自然把所有方法都继承过去了,只不过没有显示出来,
            // 其实是隐式的存在的,子类调用的其实是继承自父类的没有重写的方法,
            // 看起来像是调用了父类的方法
        }
        Shape s = new Recf();//这里是声明了一个Shape类型的引用,但实际的对象还是Recf.
        s.draw();//输出的是Recf类重写的方法,证明多态对新类的作用于在旧类中是一样的
    }
}
class Shape{//基类
    public void draw(){}
    public void erase(){}
    public void newMethod(){
        System.out.println("new method");//添加的新方法
    }
}
class Circle extends Shape{
    @Override//添加注解,一般IDE可以自动添加
    public void draw() {
        System.out.println("draw circle");
    }
    @Override
    public void erase() {
        System.out.println("erase circle");
    }
}
class Square extends Shape{
    @Override
    public void draw() {
        System.out.println("draw Square");
    }
    @Override
    public void erase() {
        System.out.println("erase Square");
    }
    @Override
    public void newMethod() {
        System.out.println("Square new method");
        //重写后该类输出内容就发生改变,没有重写时该类的该方法与父类运行结果相同
        //无论重写与否,其实调用的都是自身的方法
        //只是没有重写时方法调用结果与父类的相同
    }
}
class Triangle extends Shape{
    @Override
    public void draw() {
        System.out.println("draw Triangle");
    }
    @Override
    public void erase() {
        System.out.println("erase Triangle");
    }
}
class Recf extends Shape{//新添加的方法
    @Override
    public void draw() {
        System.out.println("recf draw");
    }
    @Override
    public void erase() {
        System.out.println("recf erase");
    }
}
class RandonShapeGenerator{//是一种工厂,用以随机获取一种Shape的子类
    private Random rand = new Random(100);
    public Shape next(){
        switch (rand.nextInt(3)){
            default:
            case 0:return new Circle();
            case 1:return new Square();
            case 2:return new Triangle();
        }
    }
}

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

public void wheels(){
        System.out.println("轮子数量是" + num);
    }

然后在main中:

        unicycle.wheels();
        bicycle.wheels();
        tricycle.wheels();

最后输出结果都顺利输出了方法中的语句,证明多态确实起作用了。

练习(6):修改Music3.java,是what()方法成为根Object的toString()方法。试用System.out.pringtln()方法打印Instrument对象(不用向上转型).
练习(7):向Music3.java添加一个新的类型Instrument,并验证多态性是否作用于所添加的新类型.
练习(8):修改Music3.java,使其可以向Shapes.java中的方式那样随机创建Instrument对象。
三个练习将在一份代码完成。

public class Test678 {
    public static void main(String[] args){
        Instrument[] orchestar = {
                new Wind(),
                new Percussion(),
                new Stringed(),
                new Brass(),
                new Woodwing()
        };
        tuneAll(orchestar);
        newInstrument ni = new newInstrument();
        ni.play(Note.B_FLAT);//验证多态性是否适用于所添加的新类型,答案是确实适用。
    }
    public static void tune(Instrument instrument){
        instrument.play(Note.MIDDLE_C);//无论传进声明子类,都播放MIDDLE_C
    }
    public static void tuneAll(Instrument[] e){
        for (Instrument i :
                e) {
            tune(i);
            System.out.println(i.toString());
        }
    }
}
class RandomInsFactory{//工厂类,用于随机生成一个Instrument的子类
    private Random ran = new Random(47);
    public Instrument next(){
        switch (ran.nextInt(5)){
            default:
            case 0:return new Wind();
            case 1:return new Percussion();
            case 2:return new Stringed();
            case 3:return new Brass();
            case 4:return new Woodwing();
            case 5:return new newInstrument();
        }
    }
}
enum Note{//枚举类,存放了哪些音乐
    MIDDLE_C,C_HARPE,B_FLAT;
}
class Instrument {
    void play(Note note){
        System.out.println("Instrument.play() : " + note);
    }
    String what(){
        return "Instrument";
    }
    void adjust(){
        System.out.println("adjusting Instrument");
    }
    @Override
    public String toString() {
        // /添加一个toString方法,调用当前what方法,
        // 子类会自动继承该方法并分别返回给自what()里的内容
        return what();
    }
}
class Wind extends Instrument{
    @Override
    void play(Note note) {
        System.out.println("Wind.play() : " + note);
    }
    @Override
    String what() {
        return "Wind";
    }
    @Override
    void adjust() {
        System.out.println("adjusting Wind");
    }
}
class Percussion extends Instrument{
    @Override
    void play(Note note) {
        System.out.println("Percussion.play() : " + note);
    }
    @Override
    String what() {
        return "Percussion";
    }
    @Override
    void adjust() {
        System.out.println("adjusting Percussion");
    }
}
class Stringed extends Instrument{
    @Override
    void play(Note note) {
        System.out.println("Stringed.play() : " + note);
    }
    @Override
    String what() {
        return "Stringed";
    }
    @Override
    void adjust() {
        System.out.println("adjusting Stringed");
    }
}
class Brass extends Wind{//继承自Wind
    @Override
    void play(Note note) {
        System.out.println("Brass.play() : " + note);
    }
    @Override
    void adjust() {
        System.out.println("adjusting Brass");
    }
}
class Woodwing extends Wind{
    @Override
    void play(Note note) {
        System.out.println("Woodwing.play() : " + note);
    }
    @Override
    String what() {
        return "Woodwing";
    }
}
class newInstrument extends Instrument{//新添加的类型
    @Override
    void play(Note note) {
        System.out.println("newIns.play()" + note);
    }
}

练习(9):创建Rodent(啮齿动物):Mouse(老鼠),Gerbil(鼹鼠),Hamster(大颊鼠),等等这样一个的继承层次结构。在基类中,提供对所有的Rodent都通用的方法,在导出类中,根据特定的Rodent类型覆盖这些方法,以便观察它们执行不同的行为。创建一个Rodent数组,填充不同的Rodent类型,然后调用基类方法,观察发生了什么情况。
这跟前面Instrument的例子相似,在Instrument中有what()这个对所有Instrument都通用的方法,而在每个子类中我们都覆盖了这个方法并赋予了不同的行为,最终在main中创建了Instrument数组,调用了基类方法,最后得到的结果是不同类调用基类方法得到的输出是该类重写后的结果。不再重复。

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

public class Test10 {
    public static void main(String[] args){
        jilei j = new daochulei();//创建导出类的对象并转型为基类
        j.first();//调用第一个方法
        //结果输出daochulei is running
        //原因,就像前面提过的,导出类继承了基类的所有东西,没有重写的方法隐藏了起来
        //其实在daochulei中还隐士的有void first()这个方法里调用了自身重写的second()
        //当daochulei调用first()方法后,它就调用了自身重写的second()方法。
        //导出类调用基类方法其实不是真的调用,而是调用自身继承自基类的方法,
        // 只不过这个方法没重写时,内部形式与基类相同
    }
}
class jilei{
    void first(){//调用第二个方法
        second();
    }
    void second(){
        System.out.println("first is running");
    }
}
class daochulei extends jilei{
    @Override
    void second() {
        System.out.println("daochulei is running");
    }
}

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

public class Test912 {
    public static void main(String[] args){
        new Hamster();
    }
}
class Rodent{
    public Rodent(){
        shanghai = 100;
        System.out.println("Rodent");
    }
    private int shanghai;
    public void bite(){
        System.out.println("造成伤害" +shanghai + "点" );
    }
}
class Mouse extends Rodent{
    private int sh;
    public Mouse(){
        sh = 1000;
        System.out.println("Mouse");
    }
    @Override
    public void bite() {
        System.out.println("造成伤害" +sh + "点" );
    }
}
class Gerbil extends Mouse{
    private int shang;
    public Gerbil(){
        shang = 2000;
        System.out.println("Gerbil");
    }
    @Override
    public void bite() {
        System.out.println("造成伤害" +shang + "点" );

    }
}
class Hamster extends Gerbil{
    private Mouse mouse = new Mouse();//成员对象
    //该类初始化输出结果
    //Rodent
//    Mouse
//    Gerbil
//    Rodent
//    Mouse
//    Hamster
    //可以分析出,初始化时先调用基类的构造方法,
    // 然后初始化成员变量,因为其中有Mouse这个成员对象,所有对Mouse进行初始化,
    // 完成后再调用自身的构造方法
    private int hai;
    public Hamster(){
        hai = 3000;
        System.out.println("Hamster");
    }
    @Override
    public void bite() {
        System.out.println("造成伤害" + hai + "点" );

    }
}
  • 3
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值