Java中的继承和抽象类

Java中的继承和抽象类

继承

在面向对象程序设计中,继承(inheritance)是一个重要的概念。继承的基本思想是,可以基于已有的类创建新的类。继承已存在的类就是复用这些类的方法,而且可以增加一些新的方法和字段,使新类能够适应新的情况。

已有的类称为超类(superclass),继承它的新类称为子类(subclass)。子类能够拥有超类的方法,而且还能新增一些自己特有的方法。在《Java 核心技术·卷Ⅰ》中以员工和经理举例(P160),代码如下

先定义一个员工类Employee,它属于超类

package inheritance;

import java.time.*;

public class Employee {
    private String name;
    private double salary;
    private LocalDate hireDay;

    public Employee(String name, double salary, int year, int month, int day)
    {
        this.name = name;
        this.salary = salary;
        hireDay = LocalDate.of(year, month, day);
    }

    public String getName()
    {
        return name;
    }

    public double getSalary()
    {
        return salary;
    }

    public LocalDate getHireDay()
    {
        return hireDay;
    }

    public void raiseSalary(double byPercent)
    {
        double raise = salary * byPercent /100;
        salary += raise;
    }

}

在定义一个经理类Manager继承员工类,因为经理也是员工

package inheritance;

public class Manager extends Employee {
    private double bonus;

    /**
     * @param name the employee's name
     * @param salary the salary
     * @param year the hire year
     * @param month the hire month
     * @param day the hire day
     */
    public Manager(String name, double salary, int year, int month, int day)
    {
        super(name, salary, year, month, day);
        bonus = 0;
    }

    public double getSalary()
    {
        double baseSalary = super.getSalary();
        return baseSalary + bonus;
    }

    public void setBonus(double bonus)
    {
        this.bonus = bonus;
    }
}

经理比普通员工多了奖金收入。

package inheritance;

/**
 * This program demonstrates inheritance.
 * @version 1.21 2022-02-06
 * @author syuez
 */
public class ManagerTest {
    public static void main(String[] args)
    {
        //construct a Manager object
        var boss = new Manager("Syuez", 80000, 1990, 8, 7);
        boss.setBonus(5000);

        var staff = new Employee[3];

        // fill the staff array with Manager and Employee objects
        staff[0] = boss;
        staff[1] = new Employee("Harry Hacker", 50000, 1989, 10, 1);
        staff[2] = new Employee("Tommy Tester", 40000, 1990, 3, 15);

        // print out information about all Employee objects
        for(Employee e : staff)
            System.out.println("name=" + e.getName() + ",salary=" + e.getSalary());
    }
}

这个示例中,还使用了多态(polymorphism),for(Employee e : staff)中的e既可以引用Employee类型的对象,也可以引用Manager类型的对象。

e引用Employee对象时,e.getSalary()调用的是Employee类中的getSalary方法;当e引用Manager对象时,e.getSalary()调用的是Manager类中的getSalary方法。虚拟机知道e实际引用的对象类型,因此能够正确地调用相应的方法。

抽象类

在此之前,我一直不太明白抽象类的意义是什么,直到我看了书上的示例代码时明白了(这真是一本好书啊)。

这里接上继承的例子,定义了一个更具有普遍意义的抽象类Person

package abstractClasses;

public abstract class Person {
    public abstract String getDescription();
    private String name;

    public Person(String name)
    {
        this.name = name;
    }

    public String getName()
    {
        return name;
    }
}

抽象类Person很简单,除了定义具体的字段和方法之外,还定义了一个抽象方法getDescription(),使用abstract关键字描述的抽象方法不需要具体实现。抽象方法充当占位方法的角色,它们在子类中具体实现。即使不含抽象方法,也可以将类声明为抽象类。

分别定义一个Employee类和Student类来继承抽象类Person

package abstractClasses;

import java.time.*;

public class Employee extends Person {
    private double salary;
    private LocalDate hireDay;

    public Employee(String name, double salary, int year, int month, int day)
    {
        super(name);
        this.salary = salary;
        hireDay = LocalDate.of(year, month, day);
    }

    public double getSalary()
    {
        return salary;
    }

    public LocalDate getHireDay()
    {
        return hireDay;
    }

    @Override
    public String getDescription()
    {
        return String.format("an employee with a salary of $%.2f",salary);
    }

    public void raiseSalary(double byPercent)
    {
        double raise = salary * byPercent / 100;
        salary += raise;
    }
}
package abstractClasses;

public abstract class Person {
    public abstract String getDescription();
    private String name;

    public Person(String name)
    {
        this.name = name;
    }

    public String getName()
    {
        return name;
    }
}
package abstractClasses;

/**
 * This program demonstrates abstract classes.
 * @version 1.01 2022-02-07
 * @author syuez
 */

public class PersonTest {

    public static void main(String[] args)
    {
        var people = new Person[2];
        // fill the pepole array with Student and Employee objects
        people[0] = new Employee("Harry Hacker", 50000, 1989, 10, 1);
        people[1] = new Student("Maria Morris", "computer science");

        // print out names and descriptions of all Person objects
        for(Person p : people)
            System.out.println(p.getName() + "," + p.getDescription());
    }
}

p.getDescription()可能让人疑惑,这不是调用了一个没有定义的方法吗?由于不能构造抽象类Person的对象,所以变量p永远不会引用Person对象,而是引用诸如EmployeeStudent这样的具体子类对象,而这些对象中都定义了getDescription()方法。

是不是和继承中的e异曲同工呢?e体现了多态,变量e可以是任意一个子类对象变量,而p又代表了超类也可以调用不同子类对象中的方法。