2022-07-28 Java第5章练习——多态、组合与继承
2024-04-10 04:40:05  阅读数 499

题目4

改写第3题的程序,利用组合来实现类复用。

由于已经建立了Person类和它的info方法,因此我再创建一个Teacher类时可以直接将Person组合进来,调用其info方法,源代码如下:

package pe;
/**
 * 此为一个Teacher类,演示了通过组合获得Person类的info方法
 * @author Liu XueZheng
 * @version 1.0
 */
public class Teacher{
    //创建一个Person实例
    private Person p;
    //定义构造器,形参为Person
    public Teacher(Person p){
    //将传入的实参赋给实例变量p
    this.p = p;
    }
    //创建一个info方法,里面调用p的info方法
    public void info(String name){
    p.info(name);
    }
    public static void main(String[] args){
        //此时需要显示创建被组合的对象
    //Object p = new Person();
    var p = new Person();
    //新建一个Teacher对象
        var t = new Teacher(p);
    //调用t的info方法
    t.info("xiaoTian");
    }
}

这里将Teacher类也放入了pe包内,在调用Person类时便可省略路径名称。需要注意的一点是在main方法中创建实例时,如果是采用Object p = new Person(); 会引起编译错误。这牵扯到了多态的问题,等式左边声明了p是一个Object类的实例,但等式右边说明实际执行时p是一个Person类实例,在编译时是按照声明的类型来的,因此会造成后边将p作为实参传入var t = new Teacher(p)时报错,错误如下:


多态引发的编译异常.png

原因就是Tracher构造器方法中的形参是一个Person类型,而传入的p在编译时是一个Object类型。
将Object改为var后便不存在这个问题,因为var会自动根据等式右边类型推测编译时类型。程序运行结果如下:


题目4程序运行结果

题目5

定义交通工具、汽车、火车、飞机这些类,注意它们的继承关
系,为这些类提供超过3个不同的构造器,并通过实例初始化块提取构
造器中的通用代码。
首先定义一个交通工具类作为父类,其具有使用寿命、单价、容纳人数三个成员变量。然后创建一个汽车类继承交通工具类。在交通工具类中利用实例初始化块对成员变量进行了初始化。源代码如下:

package vh;
//定义交通工具类
class Vehicle{
    //定义使用寿命
    protected int serviceLife;
    //定义单价
    protected double unitPrice;
    //定义容纳人数
    protected int capacity;
    //定义启动方法
    public void start(){
    System.out.println("Start");
    }
    //定义实例初始化块为成员变量赋值
    {
        serviceLife = 20;
        unitPrice = 10.0;
        capacity = 12;
    }
    //定义无参构造器
    public Vehicle(){
    System.out.println("这是Vehicle类的一个无参构造器");
    }
    //定义一个参数的构造器
    public Vehicle(String firstParam){
    System.out.println("这是Vehicle类的一个参数的构造器,参数为:" + firstParam);
    }
    public Vehicle(String firstParam, String secondParam){
    System.out.println("这是Vehicle类的两个参数的构造器,参数为:" + firstParam + ", " + secondParam);
    }
}
//定义汽车类
class Car extends Vehicle{
    //定义输出信息的info方法
    public void info(){
        System.out.println("Car的使用寿命为:" + serviceLife + "年");
        System.out.println("Car的单价为:" + unitPrice + "万");
        System.out.println("Car的容纳人数为:" + capacity + "人");
    }
}

public class TestVehicle{
    public static void main(String[] args){
        //定义一个Vehicle实例,使用无参构造器
        var v = new Vehicle();
        //定义一个Vehicle实例,使用一个参数的构造器
        var vOne = new Vehicle("one");
        //定义一个Vehicle实例,使用两个参数的构造器
        var vTwo = new Vehicle("one", "two");
        //定义一个Car实例,使用默认构造器
    var car = new Car();
    //调用car的info方法
    car.info();
    }
}

输出结果如下:


题目5程序运行结果

这里由于Car类定义时没有定义构造器,因此其默认继承了父类的构造器,相当于系统自动为Car类创建了一个无参构造器,构造器中调用了父类的无参构造器,如下所示:

public Car(){
    //调用父类的无参构造器
    super();
}

如果想调用父类的其他构造器,需要传入对应参数。比如让Car类调用父类的一个参数的构造器,代码如下:

public Car(String oneParam){
    //调用父类的一个参数的构造器
    super(oneParam);
}

运行结果如下:


Car的一个参数构造器

在Car中再新建一个三个参数的构造器,使其可以对三个成员变量赋值

public Car(int serviceLife, double unitPrice, int capacity){
        this.serviceLife = serviceLife;
    this.unitPrice = unitPrice;
        this.capacity = capacity; 
}

新建一个Car对象并利用三个参数的构造器进行初始化,打印其成员变量值

var carTwo = new Car(15, 10, 6);
carTwo.info();

结果如下:


新构造器运行结果

由于新构造器没有显示调用父类构造器,因此其隐式地调用了父类的无参构造器,然后才执行子类的新构造器。