继承是面向对象程序设计的一个基本概念。继承的基本思想是,可以基于已有的类创建新的类。继承已存在的类就是复用(继承)这些类的方法,而且可以增加一些新的方法和字段,使新类能够适应新的情况。
1 定义子类
在定义新类时,使用关键字extends
表示继承。例如:
public class Manager extends Employee
{
private double bonus;
public void setBonus(double bonus)
{
this.bonus = bonus;
}
}
关键词extends
表明正在构造的新类派生于一个已经存在的类。这个已存在的类称为超类、基类或父类,新类称为子类或派生类。在上面的例子中,Manager
类派生于Employee
类,Employee
类是超类,Manager
类是子类。
在Manager
类中,增加了一个用于存储奖金信息的字段,以及一个用于设置这个字段的新方法。这里定义的方法和字段并没有什么特别之处,如果有一个Manager
对象,就可以使用setBonus
方法。但是,由于setBonus
方法不是在Employee
类中定义的,所以Employee
类的对象不能使用它。
尽管在Manager
类中没有显式地定义getName
和getHireDay
等方法,但是可以对Manager
对象使用这些方法,因为Manager
类自动地继承了超类Employee
中的这些方法。
类似地,从超类中还继承了name
、salary
、hireDay
这3个字段。这样一来,每个Manager
对象就包含了4个字段:name
、salary
、hireDay
、bonus
。
通过扩展超类定义子类的时候,只需要指出子类与超类的不同之处。因此在设计类的时候,应该将最一般的方法放在超类中,而将更特殊的方法放在子类中。
2 覆盖方法
超类中的有些方法对子类并不一定适用,为此需要提供一个新的方法来覆盖超类中的这个方法。只需要在子类的定义中重写超类中的方法,就可以完成覆盖。例如:
public class Manager extends Employee
{
...
public double getSalary()
{
...
}
...
}
Employee
类中有getSalary
方法,在Manager
类中重写这个方法就可以覆盖它。
如果超类中的方法被覆盖,又想在子类的定义中调用这个方法,可以使用关键字super
。例如Manager
类中的getSalary
方法可以定义如下:
public double getSalary()
{
double baseSalary = super.getSalary(); // 调用 Employee 类中的 getSalary 方法
return baseSalary + bonus;
}
super
关键字也可以用于访问超类的实例字段,用法与调用超类方法类似。
3 子类构造器
由于子类的构造器不能访问超类的私有字段,所以必须通过超类的构造器来初始化这些私有字段。可以利用super
语法调用这个构造器。使用super
调用构造器的语句必须是子类构造器的第一条语句。例如Manager
类的构造器可以定义如下:
public Manager(String name, double salary, int year, int month, int day)
{
super(name, salary, year, month, day);
bonus = 0;
}
如果子类的构造器没有显式地调用超类的构造器,将自动调用超类的无参数构造器。如果超类没有无参数构造器,并且在子类中又没有显式地调用超类的其他构造器,Java编译器就会报告一个错误。
4 继承层次
继承并不仅限于一个层次。由一个公共超类派生出来的所有类的集合称为继承层次。在继承层次中,从某个特定的类到其祖先的路径称为该类的继承链。例如:
Employee
作为公共超类,派生出Manager
、Secretary
、Programmer
三个子类,Manager
类又派生出Executive
类。Manager
、Secretary
、Programmer
、Executive
这四个类的祖先都是Employee
类,称为Employee
类的继承层次。从Executive
类到祖先的路径是Executive -> Manager -> Employee
,这是Executive
类的继承链。
5 多态
里氏替换原则(Liskov Substitution Principle,LSP):程序中出现超类对象的任何地方都可以使用子类对象替换。
在Java中,对象变量是多态的。一个类的变量既可以引用本类对象,也可以引用它的任何一个子类的对象。例如上面的Employee
类变量既可以引用Employee
类对象,也可以引用Manager
、Secretary
、Programmer
、Executive
类的对象。
当子类覆盖了超类的方法时,虚拟机可以在运行时根据对象变量引用的对象类型正确地调用相应类的方法。
下面用一个例子展示多态性的应用。
/* ManagerTest.java */
package inheritance;
public class ManagerTest
{
public static void main(String[] args)
{
Manager boss = new Manager("Carl Cracker", 80000, 1987, 12, 15);
boss.setBonus(5000);
Employee[] staff = new Employee[3];
staff[0] = boss;
staff[1] = new Employee("Harry", 50000, 1989, 10