Java教程

[读书笔记]Java编程思想第8章之多态

本文主要是介绍[读书笔记]Java编程思想第8章之多态,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!
  1. 多态是继数据抽象和继承之后的第三种基本特征。多态通过分离做什么和怎么做,从另一角度将接口和实现分离开来。多态不仅能够改善代码的组织结构和可读性,还可以创建可扩展程序。“封装”通过合并特征和行为来创建新的数据类型;“实现隐藏”则通过将细节“私有化”把接口和实现分离开来;多态的作用则是消除类型之间的耦合关系。多态方法允许一种类型表现出与其它相似类型之间的区别,只要它们都是从同一基类导出而来的。
  2. 将一个方法调用同一个方法主体关联起来被称作绑定。若在程序执行前进行绑定(如果有的话,由编译器和连接程序实现),叫做前期绑定。所谓后期绑定:它的含义就是在运行时根据对象的类型进行绑定,后期绑定也叫做动态绑定或运行时绑定。后期绑定机制随编程语言的不同而有所不同,但不管怎样都必须在对象中安置某种“类型信息”。Java中除了static方法和final方法(private方法属于final方法)之外,其他所有的方法都是后期绑定。
  3. 一旦知道Java中所有方法都是通过动态绑定实现多态这个事实之后,我们就可以编写只与基类打交道的程序代码了。或者换一种说法,发送消息给某个对象,让该对象去判定应该做什么事。在编译时,编译器不需要获得任何特殊信息就能进行正确的调用。
  4. 在一个设计良好的OPP程序中,大多数或者所有方法都会基类接口通信。这样的程序是可扩展的,因为可以从通用的基类继承出新的数据类型,从而新添一些功能。那些操作基类接口的方法不需要任何改动就可以应用于新类。多态是一项让程序员“将改变的事物与未变的事物分离开来”的重要技术。
  5. 多态的缺陷:“覆盖私有方法”;“直接访问某个域”;“访问静态的方法”;只有普通的方法调用可以是多态的。任何域访问操作都将由编译器解析,因此不是多态的。如果某个方法是静态的,它的行为就不具有多态性。静态方法是与类,而并非与某个对象相关联的。
package thinkinginjava.charpenter8;

/**
 * @author Spring-_-Bear
 * @version 9/29/21 5:54 PM
 */
public class PrivateOverride {
    private void f(){
        System.out.println("private f()");
    }

    public static void main(String[] args) {
        PrivateOverride privateOverride = new Derived();
        privateOverride.f();
    }
}

class Derived extends PrivateOverride{
    public void f(){
        System.out.println("public f()");
    }
}
package thinkinginjava.charpenter8;

/**
 * @author Spring-_-Bear
 * @version 9/29/21 5:57 PM
 */
public class FieldAccess {
    public static void main(String[] args) {
        /**
         * Upcast
         */
        Super sup = new Sub();
        System.out.println("sup.field = " + sup.field + ", sup.getField() = " + sup.getField());

        Sub sub = new Sub();
        System.out.println("sup.field = " + sub.field + ", sup.getField() = " + sub.getField() + ", sum.getSuperField() = " + sub.getSuperField());

    }
}

class Super {
    public int field = 0;

    public int getField() {
        return field;
    }
}

class Sub extends Super {
    public int field = 1;

    @Override
    public int getField() {
        return field;
    }

    public int getSuperField() {
        return super.field;
    }
}
package thinkinginjava.charpenter8;

/**
 * @author Spring-_-Bear
 * @version 9/29/21 6:07 PM
 */
public class StaticPolymorphism {
    public static void main(String[] args) {
        /**
         * Upcast
         */
        StaticSuper sup = new StaticSub();
        System.out.println(sup.staticGet());
        System.out.println(sup.dynamicGet());
    }
}

class StaticSuper{
    public static String staticGet() {
        return "Base staticGet()";
    }

    public String dynamicGet() {
        return "Base dynamicGet()";
    }
}

class StaticSub extends StaticSuper{
    public static String staticGet() {
        return "Derived staticGet()";
    }

    @Override
    public String dynamicGet() {
        return "Derived dynamicGet()";
    }
}
  1. 构造器并不具有多态性(构造器实际是static方法,只不过该static声明是隐式的)。基类的构造器总是在导出类的构造过程中被调用,而且按照继承层次逐渐向上链接,以使每个基类的构造器都能得到调用。调用构造器要遵循下面的顺序:
    1) 调用基类的构造器。
    2) 按声明顺序调用成员的初始化方法。
    3) 调用导出类构造器的主体。
package thinkinginjava.charpenter8;

/**
 * @author Spring-_-Bear
 * @version 9/29/21 6:18 PM
 */
public class Sandwich extends PortableLunch {
    private Bread bread = new Bread();
    private Cheese cheese = new Cheese();
    private Lettuce lettuce = new Lettuce();

    public Sandwich() {
        System.out.println("Sandwich()");
    }

    public static void main(String[] args) {
        new Sandwich();
    }
}

class Meal{
    Meal() {
        System.out.println("Meal()");
    }
}

class Bread{
    Bread() {
        System.out.println("Bread()");
    }
}

class Cheese{
    Cheese() {
        System.out.println("Cheese()");
    }
}

class Lettuce{
    Lettuce(){
        System.out.println("Lettuce()");
    }
}

class Lunch extends Meal{
    Lunch() {
        System.out.println("Lunch()");
    }
}

class PortableLunch extends Lunch{
    PortableLunch() {
        System.out.println("PortableLunch()");
    }
}
  1. 动态绑定的调用是在运行时才决定的,因为对象无法知道它是属于方法所子的那个类,还是属于那个类的导出类。如果要调用构造器内部的一个动态绑定方法,就要用到那个方法被覆盖后的定义。然而,这个调用的效果可能相当难以预料,因为被覆盖的方法在对象完全构造之前就会被调用。这可能会造成一些难以预料的错误。构造器的工作实际是创建对象(这并非是一件平常的工作)。如果构造器只是在对象构造过程中的一个步骤,并且该对象所属的类是从这个构造器所属的类导出的,那么导出部分在当前构造器正在被调用的时刻仍然是未初始化的。
package thinkinginjava.charpenter8;

/**
 * @author Spring-_-Bear
 * @version 9/29/21 7:14 PM
 */
public class PolyConstructors {
    public static void main(String[] args) {
        new RoundGlyph(5);
    }
}

class Glyph {
    void draw() {
        System.out.println("Sup-Glyph.draw()");
    }

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

class RoundGlyph extends Glyph {
    private int radius = 1;

    RoundGlyph(int radius) {
        this.radius = radius;
        System.out.println("RoundGlyph.RoundGlyph(), radius = " + radius);
    }

    @Override
    void draw() {
        System.out.println("Sub-RoundGlyph.draw(), radius = " + radius);
    }
}

初始化的实际过程:
1)在其他任何事物发生之前,将分配给对象的存储空间初始化为二进制的0;
2)如前所述那样调用基类构造器。此时,调用被覆盖后的draw()方法(要在调用RoundGlyph构造器之前调用),由于步骤1的缘故,此时radius为0;
3)按照声明的顺序调用成员的初始化方法;
4)调用导出类的构造主体。

  1. 编写构造器一条有效的准则:“用尽可能简单的方法使对象进入正常状态;如果可以的话,避免调用其它方法。”在构造器内唯一能够安全调用的那些方法就是基类中的final方法(也适用于private方法)。这些方法不能被覆盖,因此不会出现如此“离谱”的问题。
  2. Java SE5中添加了协变返回类型,它表示在导出类的被覆盖方法中可以返回基类方法的返回类型的某种导出类型。
package thinkinginjava.charpenter8;

/**
 * @author Spring-_-Bear
 * @version 9/29/21 7:29 PM
 */
public class CovariantReturn {
    public static void main(String[] args) {
        Mill mill  = new Mill();
        Grain grain = new Grain();
        System.out.println(grain);
        mill = new WheatMill();
        grain = mill.process();
        System.out.println(grain);
    }
}

class Grain{
    @Override
    public String toString() {
        return "Grain";
    }
}

class Wheat extends Grain{
    @Override
    public String toString() {
        return "Wheat";
    }
}

class Mill{
    Grain process() {
        return new Grain();
    }
}

class WheatMill extends Mill{
    @Override
    Wheat process() {
        return new Wheat();
    }
}
  1. 事实上,当我们使用现成的类来建立新类时,如果首先考虑使用继承技术,反倒会加重我们的设计负担,使事情变得不必要地复杂起来。一条通用的准则是:“用继承表达行为间的差异,并用字段表达状态上的变化”。
  2. 向下转型与运行时类型识别(RTTI),RTTI的内容不仅仅包括转型处理。例如它还提供一种方法,使你可以在试图向下转型之前,查看你所要处理的类型。
package thinkinginjava.charpenter8;

/**
 * @author Spring-_-Bear
 * @version 9/29/21 7:43 PM
 */
public class RTTI {
    public static void main(String[] args) {
        Useful[] usefuls = {
                new Useful(),
                new MoreUseful()
        };

        usefuls[0].f();
        usefuls[1].g();
        /**
         * Downcast/RTTI
         */
        ((MoreUseful)usefuls[1]).u();
        /**
         * Exception thrown
         */
        ((MoreUseful)usefuls[0]).u();
    }
}

class Useful {
    public void f() {
    }

    public void g() {
    }
}

class MoreUseful extends Useful {
    @Override
    public void f() {
    }

    @Override
    public void g() {
    }

    public void u() {
    }

    public void v() {
    }

    public void w() {
    }
}
  1. 如果不运用数据抽象和继承,就不可能理解或者甚至不可能创建多态的例子。为了有效地运用多态乃至面向对象技术,必须拓展自己的编程视野,使其不仅包括个别类的成员和消息,而且还要包括类与类之间的共同特征以及它们之间的关系。

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

package thinkinginjava.charpenter8;

/**
 * @author Spring-_-Bear
 * @version 9/29/21 7:59 PM
 */
public class Biking {
    public static void ride(Cycle cycle) {
        cycle.travel("" + cycle);
    }

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

class Cycle {
    public void travel(String s) {
        System.out.println("Cycle.travel " + s);
    }
}

class Unicycle extends Cycle {
    @Override
    public String toString() {
        return "Unicycle";
    }
}

class Bicycle extends Cycle {
    @Override
    public String toString() {
        return "Bicycle";
    }
}

class Tricycle extends Cycle {
    @Override
    public String toString() {
        return "Tricycle";
    }
}

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

package thinkinginjava.charpenter8;

import java.util.Random;

/**
 * @author Spring-_-Bear
 * @version 2021/09/28 7:44 PM
 */
public class Shapes {
    private static RandomShapeGenerator generator = new RandomShapeGenerator();

    public static void main(String[] args) {
        Shape[] shapes = new Shape[9];
        /**
         * Fill up the array with Shape references
         */
        for (int i = 0; i < shapes.length; i++) {
            shapes[i] = generator.next();
        }

        for (Shape shp : shapes) {
            shp.draw();
        }
    }
}

class Shape {
    public void draw() {
    }

    public void erase() {
    }
}

class Circle extends Shape {
    @Override
    public void draw() {
        System.out.println("Circle.draw()");
    }

    @Override
    public void erase() {
        System.out.println("Circle.erase()");
    }
}

class Square extends Shape {
    @Override
    public void draw() {
        System.out.println("Square.draw()");
    }

    @Override
    public void erase() {
        System.out.println("Square.erase()");
    }
}

class Triangle extends Shape {
    @Override
    public void draw() {
        System.out.println("Triangle.draw()");
    }

    @Override
    public void erase() {
        System.out.println("Triangle.erase()");
    }
}

class RandomShapeGenerator {
    private Random random = new Random(55);

    public Shape next() {
        switch (random.nextInt(3)) {
            case 0:
                return new Circle();
            case 1:
                return new Square();
            case 2:
                return new Triangle();
            default:
        }
        
        return null;
    }
}

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

package thinkinginjava.charpenter8;

import java.util.Random;

/**
 * @author Spring-_-Bear
 * @version 2021/09/28 7:44 PM
 */
public class Shapes {
    private static RandomShapeGenerator generator = new RandomShapeGenerator();

    public static void main(String[] args) {
        Shape[] shapes = new Shape[9];
        /**
         * Fill up the array with Shape references
         */
        for (int i = 0; i < shapes.length; i++) {
            shapes[i] = generator.next();
        }

        for (Shape shp : shapes) {
            shp.draw();
            shp.erase();
            shp.amend();
        }
    }
}

class Shape {
    public void draw() {
        System.out.println("Shape.draw()");
    }

    public void erase() {
        System.out.println("Shape.amend()");
    }

    public void amend() {
        System.out.println("Shape.amend()");
    }
}

class Circle extends Shape {
    @Override
    public void draw() {
        System.out.println("Circle.draw()");
    }

    @Override
    public void erase() {
        System.out.println("Circle.erase()");
    }

    @Override
    public void amend() {
        System.out.println("Circle.amend()");
    }
}

class Square extends Shape {
    @Override
    public void draw() {
        System.out.println("Square.draw()");
    }

    @Override
    public void erase() {
        System.out.println("Square.erase()");
    }

    @Override
    public void amend() {
        System.out.println("Square.amend()");
    }
}

class Triangle extends Shape {
    @Override
    public void draw() {
        System.out.println("Triangle.draw()");
    }

    @Override
    public void erase() {
        System.out.println("Triangle.erase()");
    }

    @Override
    public void amend() {
        System.out.println("Triangle.amend()");
    }
}

class RandomShapeGenerator {
    private Random random = new Random(55);

    public Shape next() {
        switch (random.nextInt(3)) {
            case 0:
                return new Circle();
            case 1:
                return new Square();
            case 2:
                return new Triangle();
            default:
        }

        return null;
    }
}

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

package thinkinginjava.charpenter8;

import java.util.Random;

/**
 * @author Spring-_-Bear
 * @version 2021/09/28 7:44 PM
 */
public class Shapes {
    private static RandomShapeGenerator generator = new RandomShapeGenerator();

    public static void main(String[] args) {
        Shape[] shapes = new Shape[9];
        /**
         * Fill up the array with Shape references
         */
        for (int i = 0; i < shapes.length; i++) {
            shapes[i] = generator.next();
        }

        for (Shape shp : shapes) {
            shp.draw();
            shp.erase();
            shp.amend();
        }
    }
}

class Shape {
    public void draw() {
        System.out.println("Shape.draw()");
    }

    public void erase() {
        System.out.println("Shape.amend()");
    }

    public void amend() {
        System.out.println("Shape.amend()");
    }
}

class Circle extends Shape {
    @Override
    public void draw() {
        System.out.println("Circle.draw()");
    }

    @Override
    public void erase() {
        System.out.println("Circle.erase()");
    }

    @Override
    public void amend() {
        System.out.println("Circle.amend()");
    }
}

class Square extends Shape {
    @Override
    public void draw() {
        System.out.println("Square.draw()");
    }

    @Override
    public void erase() {
        System.out.println("Square.erase()");
    }

    @Override
    public void amend() {
        System.out.println("Square.amend()");
    }
}

class Triangle extends Shape {
    @Override
    public void draw() {
        System.out.println("Triangle.draw()");
    }

    @Override
    public void erase() {
        System.out.println("Triangle.erase()");
    }

    @Override
    public void amend() {
        System.out.println("Triangle.amend()");
    }
}

class RandomShapeGenerator {
    private Random random = new Random(55);

    public Shape next() {
        switch (random.nextInt(4)) {
            case 0:
                return new Circle();
            case 1:
                return new Square();
            case 2:
                return new Triangle();
            case 3:
                return new Oval();
            default:
        }

        return null;
    }
}

class Oval extends Shape {
    @Override
    public void draw() {
        System.out.println("Oval.draw()");
    }

    @Override
    public void erase() {
        System.out.println("Oval.erase()");
    }

    @Override
    public void amend() {
        System.out.println("Oval.amend()");
    }
}

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

package thinkinginjava.charpenter8;

/**
 * @author Spring-_-Bear
 * @version 9/29/21 7:59 PM
 */
public class Biking {
    public static void ride(Cycle cycle) {
        cycle.travel("" + cycle);
        cycle.wheels();
    }

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

class Cycle {
    private int wheels = 4;

    public void travel(String s) {
        System.out.println("Cycle.travel " + s);
    }

    public void wheels() {
        System.out.println("Cycle.wheels = " + wheels);
    }
}

class Unicycle extends Cycle {
    private int wheels = 1;
    @Override
    public String toString() {
        return "Unicycle";
    }

    @Override
    public void wheels() {
        System.out.println("Unicycle.wheels = " + wheels);
    }
}

class Bicycle extends Cycle {
    private int wheels = 2;

    @Override
    public String toString() {
        return "Bicycle";
    }

    @Override
    public void wheels() {
        System.out.println("Bicycle.wheels = " + wheels);
    }
}

class Tricycle extends Cycle {
    private int wheels = 3;

    @Override
    public String toString() {
        return "Tricycle";
    }

    @Override
    public void wheels() {
        System.out.println("Tricycle.wheels = " + wheels);
    }
}

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

package thinkinginjava.charpenter8;

/**
 * @author Spring-_-Bear
 * @version 2021/09/28 8:04 PM
 */
public class Music3 {
    public static void tune(Instrument instrument) {
        instrument.play(Note.MIDDLE_C);
    }

    public static void tuneAll(Instrument[] instruments) {
        for (Instrument instrument : instruments) {
            tune(instrument);
        }
    }

    public static void main(String[] args) {
        /**
         * Upcasting during addition to the array.
         */
        Instrument[] instruments = {
          new Wind(),
          new Percussion(),
          new Stringed(),
          new Brass(),
          new Woodwind()
        };

        tuneAll(instruments);
    }
}

enum Note {
    MIDDLE_C, C_SHARP, B_FLAT;
}

class Instrument{
    void play(Note note) {
        System.out.println("Instrument.play() " + note);
    }

    @Override
    public String toString() {
        return "Instrument";
    }

    void adjust(){
        System.out.println("Adjusting Instrument");
    }
}

class Wind extends Instrument{
    @Override
    void play(Note note){
        System.out.println("Wind.play() " + note);
    }

    @Override
    public String toString() {
        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
    public String toString() {
        return super.toString();
    }

    @Override
    void adjust() {
        System.out.println("Adjusting Percussion");
    }
}

class Stringed extends Instrument{
    @Override
    void play(Note note) {
        System.out.println("Stringed.play() " + note);
    }

    @Override
    void adjust() {
        System.out.println("Adjusting Stringed");
    }

    @Override
    public String toString() {
        return "Stringed";
    }
}

class Brass extends Wind{
    @Override
    void play(Note note) {
        System.out.println("Brass.play() " + note);
    }

    @Override
    void adjust() {
        System.out.println("Adjusting Brass");
    }
}

class Woodwind extends Wind{
    @Override
    void play(Note note){
        System.out.println("Woodwind.play() " + note);
    }

    @Override
    public String toString() {
        return "Woodwind";
    }
}

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

package thinkinginjava.charpenter8;

/**
 * @author Spring-_-Bear
 * @version 2021/09/28 8:04 PM
 */
public class Music3 {
    public static void tune(Instrument instrument) {
        instrument.play(Note.MIDDLE_C);
    }

    public static void tuneAll(Instrument[] instruments) {
        for (Instrument instrument : instruments) {
            tune(instrument);
        }
    }

    public static void main(String[] args) {
        /**
         * Upcasting during addition to the array.
         */
        Instrument[] instruments = {
                new Wind(),
                new Percussion(),
                new Stringed(),
                new Brass(),
                new Woodwind(),
                new Piano()
        };

        tuneAll(instruments);
    }
}

enum Note {
    MIDDLE_C, C_SHARP, B_FLAT;
}

class Instrument {
    void play(Note note) {
        System.out.println("Instrument.play() " + note);
    }

    @Override
    public String toString() {
        return "Instrument";
    }

    void adjust() {
        System.out.println("Adjusting Instrument");
    }
}

class Wind extends Instrument {
    @Override
    void play(Note note) {
        System.out.println("Wind.play() " + note);
    }

    @Override
    public String toString() {
        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
    public String toString() {
        return super.toString();
    }

    @Override
    void adjust() {
        System.out.println("Adjusting Percussion");
    }
}

class Stringed extends Instrument {
    @Override
    void play(Note note) {
        System.out.println("Stringed.play() " + note);
    }

    @Override
    void adjust() {
        System.out.println("Adjusting Stringed");
    }

    @Override
    public String toString() {
        return "Stringed";
    }
}

class Brass extends Wind {
    @Override
    void play(Note note) {
        System.out.println("Brass.play() " + note);
    }

    @Override
    void adjust() {
        System.out.println("Adjusting Brass");
    }
}

class Woodwind extends Wind {
    @Override
    void play(Note note) {
        System.out.println("Woodwind.play() " + note);
    }

    @Override
    public String toString() {
        return "Woodwind";
    }
}

class Piano extends Instrument {
    @Override
    void play(Note note) {
        System.out.println("Piano.play() " + note);
    }
}

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

package thinkinginjava.charpenter8;

import java.util.Random;

/**
 * @author Spring-_-Bear
 * @version 2021/09/28 8:04 PM
 */
public class Music3 {
    public static void tune(Instrument instrument) {
        instrument.play(Note.MIDDLE_C);
    }

    public static void tuneAll(Instrument[] instruments) {
        for (Instrument instrument : instruments) {
            tune(instrument);
        }
    }

    public static void main(String[] args) {
        Instrument[] instruments = new Instrument[6];
        RandomInstrumentGenerator randomInstrumentGenerator = new RandomInstrumentGenerator();

        for (int i = 0; i < instruments.length; i++) {
            instruments[i] = randomInstrumentGenerator.next();
        }

        tuneAll(instruments);
    }
}

enum Note {
    MIDDLE_C, C_SHARP, B_FLAT;
}

class Instrument {
    void play(Note note) {
        System.out.println("Instrument.play() " + note);
    }

    @Override
    public String toString() {
        return "Instrument";
    }

    void adjust() {
        System.out.println("Adjusting Instrument");
    }
}

class Wind extends Instrument {
    @Override
    void play(Note note) {
        System.out.println("Wind.play() " + note);
    }

    @Override
    public String toString() {
        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
    public String toString() {
        return super.toString();
    }

    @Override
    void adjust() {
        System.out.println("Adjusting Percussion");
    }
}

class Stringed extends Instrument {
    @Override
    void play(Note note) {
        System.out.println("Stringed.play() " + note);
    }

    @Override
    void adjust() {
        System.out.println("Adjusting Stringed");
    }

    @Override
    public String toString() {
        return "Stringed";
    }
}

class Brass extends Wind {
    @Override
    void play(Note note) {
        System.out.println("Brass.play() " + note);
    }

    @Override
    void adjust() {
        System.out.println("Adjusting Brass");
    }
}

class Woodwind extends Wind {
    @Override
    void play(Note note) {
        System.out.println("Woodwind.play() " + note);
    }

    @Override
    public String toString() {
        return "Woodwind";
    }
}

class Piano extends Instrument {
    @Override
    void play(Note note) {
        System.out.println("Piano.play() " + note);
    }
}

class RandomInstrumentGenerator {
    private Random random = new Random(47);

    public Instrument next() {
        switch (random.nextInt(6)) {
            case 0:
                return new Wind();
            case 1:
                return new Percussion();
            case 2:
                return new Stringed();
            case 3:
                return new Brass();
            case 4:
                return new Woodwind();
            case 5:
                return new Piano();
            default:
        }

        return null;
    }
}

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

package thinkinginjava.charpenter8;

import java.util.Random;

/**
 * @author Spring-_-Bear
 * @version 2021/9/30 23:07
 */
public class RodentTester {
    private static RandomRodentGenerator generator = new RandomRodentGenerator();

    public static void main(String[] args) {
        Rodent[] rodents = new Rodent[10];
        for (Rodent rodent : rodents) {
            rodent = generator.next();
            System.out.println(rodent + ": ");
            rodent.eat();
            rodent.run();
        }
    }
}

class Rodent {
    protected void eat() {
        System.out.println("Rodent.eat()");
    }

    protected void run() {
        System.out.println("Rodent.run()");
    }

    @Override
    public String toString() {
        return "Rodent";
    }
}

class Mouse extends Rodent {
    @Override
    protected void eat() {
        System.out.println("Mouse.eat()");
    }

    @Override
    protected void run() {
        System.out.println("Mouse.run()");
    }

    @Override
    public String toString() {
        return "Mouse";
    }
}

class Gerbil extends Rodent {
    @Override
    protected void run() {
        System.out.println("Gerbil.run()");
    }

    @Override
    protected void eat() {
        System.out.println("Gerbil.eat()");
    }

    @Override
    public String toString() {
        return "Gerbil";
    }
}

class RandomRodentGenerator {
    private Random random = new Random(55);

    public Rodent next() {
        switch (random.nextInt(2)) {
            case 0:
                return new Mouse();
            case 1:
                return new Gerbil();
            default:
        }

        return null;
    }
}

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

package thinkinginjava.charpenter8;

/**
 * @author Spring-_-Bear
 * @version 2021/10/1 14:55
 */
public class PolyMethod extends SuperMethod {
    @Override
    protected void method2() {
        System.out.println("PolyMethod.method()");
    }

    public static void main(String[] args) {
        /**
         * Upcasting
         */
        SuperMethod polyMethod = new PolyMethod();
        polyMethod.method1();
    }
}

class SuperMethod{
    protected void method1(){
        System.out.println("SuperMethod.method1()");
        method2();
    }

    protected void method2() {
        System.out.println("SuperMethod.method2()");
    }
}

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

package thinkinginjava.charpenter8;

/**
 * @author Spring-_-Bear
 * @version 9/29/21 6:18 PM
 */
public class Sandwich extends PortableLunch {
    private Bread bread = new Bread();
    private Cheese cheese = new Cheese();
    private Lettuce lettuce = new Lettuce();
    private Pickle pickle = new Pickle();

    public Sandwich() {
        System.out.println("Sandwich()");
    }

    public static void main(String[] args) {
        new Sandwich();
    }
}

class Meal{
    Meal() {
        System.out.println("Meal()");
    }
}

class Bread{
    Bread() {
        System.out.println("Bread()");
    }
}

class Cheese{
    Cheese() {
        System.out.println("Cheese()");
    }
}

class Lettuce{
    Lettuce(){
        System.out.println("Lettuce()");
    }
}

class Lunch extends Meal{
    Lunch() {
        System.out.println("Lunch()");
    }
}

class PortableLunch extends Lunch{
    PortableLunch() {
        System.out.println("PortableLunch()");
    }
}

class Pickle {
    Pickle() {
        System.out.println("Pickle()");
    }
}

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

package thinkinginjava.charpenter8;

/**
 * @author Spring-_-Bear
 * @version 2021/9/30 23:07
 */
public class RodentTester {
    public static void main(String[] args) {
        Rodent mouse = new Mouse();
        System.out.println(mouse + ": ");
        mouse.eat();
        mouse.run();
        mouse.sleep();
    }
}

class Characteristic {
    private String s;

    Characteristic(String s) {
        this.s = s;
        System.out.println("Creating Characteristic " + s);
    }
}

class Description {
    private String s;

    Description(String s) {
        this.s = s;
        System.out.println("Creating Description " + s);
    }
}

class Rodent {
    private String name = "Rodent";
    private Characteristic c = new Characteristic("has tail");
    private Description d = new Description("small mammal");

    Rodent() {
        System.out.println("Rodent()");
    }

    protected void eat() {
        System.out.println("Rodent.eat()");
    }

    protected void run() {
        System.out.println("Rodent.run()");
    }

    protected void sleep() {
        System.out.println("Rodent.sleep()");
    }

    @Override
    public String toString() {
        return name;
    }
}

class Mouse extends Rodent {
    private String name = "Mouse";
    private Characteristic c = new Characteristic("likes cheese");
    private Description d = new Description("nocturnal");
    Mouse() {
        System.out.println("Mouse()");
    }
    @Override
    protected void eat() {
        System.out.println("Mouse.eat()");
    }
    @Override
    protected void run() {
        System.out.println("Mouse.run()");
    }
    @Override
    protected void sleep() {
        System.out.println("Mouse.sleep()");
    }
    @Override
    public String toString() { return name; }
}

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

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

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

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

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

这篇关于[读书笔记]Java编程思想第8章之多态的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!