Java 继承机制

类、超类和子类

定义子类

使用 extends 关键字来定义子类。在Java中所有的继承都是公有继承,子类继承父类的所有方法和实例域。

Java没有多继承。

而C++有私有继承和公有继承之分。

示例:

1
2
3
4
public class Manager extends Employee
{
// Add methods and fields
}

覆盖方法

如果子类从父类继承而来的某个方法不适合于子类的需求,可以通过重写(override)的方式重新编写这个方法。

  • super 关键字

子类从父类继承而来的方法,不能直接访问父类定义的私有域。因此如果想要访问父类定义的私有域,需要借助于 super 关键字。示例:

1
2
3
4
5
public double getSalary()
{
double baseSalary = super.getSalary();
return baseSalary + bonus;
}

super 关键字和 this 关键字不同,this 关键字是对象引用,但是 super 关键字不是对象引用,它只是一个指示编译器调用超类方法的特殊关键字。

注意:

  • private 方法不能重写
  • final 方法不能重写
  • 使用 private final 修饰方法也可以保证不能重写,但是只需要其中一个即可(private 会还导致只能类内部访问)。

子类构造器

由于子类不能访问父类定义的私有域,所以如果子类的构造器想要对父类定义的实例域进行初始化,必须在子类的构造器中调用父类相应的构造器。调用方式为 super(param1, param2, ...)。示例:

1
2
3
4
5
6
7
8
public Manager extends Employee
{
public Manager(String name, double salary, int year, int month, int day)
{
super(name, salary, year, month, day);
bonus = 0;
}
}

如果子类的构造器没有显式地调用父类的父类的构造器,则将自动地调用父类默认(没有参数)的构造器。如果父类没有不带参数的构造器,并且在子类中又没有显式地调用父类的其他构造器,则Java编译器将报告错误。

多态和动态绑定的概念

考虑以下代码:

1
2
3
4
5
6
7
8
9
Employee[] staff = new Employee[3];
staff[0] = new Manager("Carl Cracker", 80000, 1987, 12, 15)
staff[1] = new Employee("Harry Hacker", 50000, 1989, 10, 1)
staff[2] = new Employee("Tony Tester", 40000, 1990, 3, 15)

for(Employee e: staff)
{
System.out.println(e.getName() + " " + e.getSalary());
}

在上面代码的循环中, e 虽然被声明为 Employee 类型,但实际上 e 既可以引用 Employee 类型的对象,也可以引用 Manager 类型的对象。当 e 引用的是 Employee 对象的时候,它会调用 Employee 类中的 getSalary() 方法,当 e 引用的是 Manager 类型的对象的时候,它会调用 Manager 类中的 getSalary() 方法。JVM虚拟机知道 e 实际引用的对象类型,因此能够正确地调用相应的方法。

一个对象变量(例如上边的 e)可以引用多种实际类型的现象被称为 多态(polymorphism)

在运行时能够自动地选择调用哪个方法的现象称为 动态绑定(dynamic binding)

理解方法调用

使用对象引用进行方法调用的时候,是根据对象引用声明的类型来进行寻找方法的,比如上边的例子中,声明为 Employee 类型的 e 可以调用 Employee 类型的方法,也可以调用 Manager 类型继承自 Employee 的方法,但是不能调用 Manager 单独定义的方法。

也就是说,动态绑定只是针对父类和子类共有的方法而言的。

不允许继承

可以对类加上 final 关键字,表示不允许继承这个类。这个类中的方法也自动被看作 final 类型的方法。

如果对某个方法加上 final 关键字,表示不允许子类覆盖这个方法,可以保证这个方法在子类中不改变语义。

对象引用强制类型转换

如果一个对象引用的引用声明的是父类的类型,但是引用的是一个子类的对象,那么可以通过强制类型转换把这个对象引用转换为声明为子类对象的对象引用。

抽象类

可以使用 abstract 关键字把一个类声明为抽象类,抽象类不能被实例化。抽象类可以有0个,1个或者多个抽象方法。

抽象类可以拥有具体方法,但是一般不提倡这么做。

受保护访问 protected

publicprivate 修饰符之外,还可以将方法或域声明为 protected 类型,这样,子类就可以访问父类中定义的类型为 protected 类型的方法或者域。

事实上,Java中受保护的部分对所有子类及同一个包中的所有其他类都可见。

Object 类是所有类的超类

在Java中,除了基本类型(primitive type)不是对象,其他的所有类型都是对象。且全部都是 Object 类的子类。

参考资料

  1. java - Why private method can not be final as well? - Stack Overflow