Java继承
以公司雇员与经理为例
继承概念
继承就是子类继承父类的特征和行为,使得子类对象(实例)具有父类的实例域和方法,或子类从父类继承方法,使得子类具有父类相同的行为。允许创建分等级层次的类。
例子:公司雇员与经理的待遇有一些差异,但也存在很多相同的地方,例如雇员仅领取薪水,而经理可以领取薪水和奖金,这种情形就需要使用继承,这是因为需要为经理定义一个新类Manager,增加一些新功能,可以重用Employee类中的部分代码,Employee和Manager存在“is-a ”的关系。
为什么需要继承
不使用继承具有相同特性的话,需要写多份相同的代码,代码存在重复了,导致后果就是代码量大且臃肿,而且维护性不高(维护性主要是后期需要修改的时候,就需要修改很多的代码,容易出错),所以要从根本上解决这两段代码的问题,就需要继承。
定义子类
关键字extends表示继承,关键字extends表明正在构造的新类派生于一个已存在的类,已存在的类称为超类(superclass),新类称为子类(subclass),子类比超类拥有的功能更加丰富。在Manager类中,增加了一个用于存储奖金信息的域,以及一个用于设置这个域的新方法:
public class Manager extends Employee {
private double bonus;
public void setBonus(double bonus) {
this.bonus = bonus;
}
}
超类代码:
package demo;
import java.time.LocalDate;
public class Employee{
private String name;
private double salary;
private LocalDate hireDay;
public Employee(String name, double salary, int year,int month,int day) {
super();
this.name = name;
this.salary = salary;
this.hireDay = LocalDate.of(year,month,day);
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public double getSalary() {
return salary;
}
public void setSalary(double salary) {
this.salary = salary;
}
public LocalDate getHireDay() {
return hireDay;
}
public void setHireDay(LocalDate hireDay) {
this.hireDay = hireDay;
}
public void raiseSalary(double byPercent) {
double raise = salary * byPercent / 100;
salary +=raise;
}
}
尽管在Manager类中没有显式地定义Employee类中的getName和getHireDay等方法,但属于Manager类的对象却可以使用它们,这是因为Manager类自动地继承了超类Employee中的这些方法。同样,从超类中还继承了name、salary和hireDay这3个域。这样一来,每个Manager类对象就包含了4个域:name、salary、hireDay和bonus。
通用的方法放在超类,具有特殊用途的方法放在子类中。
覆盖方法
超类中的有些方法对子类Manager并不一定适用,Manager类中的getSalary方法应该返回薪水和奖金的总和,为此,需要提供一个新的方法来覆盖(override)超类中的这个方法。
public class Manager extends Employee {
private double bonus;
public void setBonus(double bonus) {
this.bonus = bonus;
}
public double getSalary() {
double baseSalary = super.getSalary();
return baseSalary + bonus;
}
}
如果 double baseSalary = super.getSalary();改为 double baseSalary =salary;或者 double baseSalary = getSalary();都不能运行,前者是因为因为Manager类的getSalary方法不能够直接地访问超类的私有域,如果一定要访问私有域,必须借助共有的接口getSalary;后者是因为Manager类也有一个getSalary方法,如果这样写会导致无限次调用自己,直到整个程序崩溃,为此可以用关键字super解决这个问题:super.getSalary(),调用的是Employee类中的getSalary方法。
super 与 this 关键字
super关键字:我们可以通过super关键字来实现对父类成员的访问,用来引用当前对象的父类。
this关键字:指向自己的引用。
子类构造器
public class Manager extends Employee {
private double bonus;
public void setBonus(double bonus) {
this.bonus = bonus;
}
public double getSalary() {
double baseSalary = super.getSalary();
return baseSalary + bonus;
}
public Manager(String name,double salary,int year,int month,int day) {
super(name,salary,year,month,day);
bonus = 0;
}
}
最后提供了一个构造器,这里的super具有不同的含义。 super(name,salary,year,month,day);是调用超类Employee中含有name,salary,year,month,day参数的构造器的简写形式。使用super调用构造器的语句必须是子类构造器的第一条语句。如果子类的构造器没有显式地调用超类的构造器,则将自动地调用超类默认(没有参数)的构造器。
Test代码:
package demo;
public class ManagerTest {
public static void main(String[] args) {
Manager boss = new Manager("Car Cracker", 80000, 1987, 12, 15);
boss.setBonus(5000);
Employee[] staff = new Employee[3];
staff[0] = boss;
staff[1] = new Employee("Harry Hacker", 50000, 1989, 10, 1);
staff[2] = new Employee("Tommy Tester", 40000, 1990, 3, 15);
for(Employee e : staff) {
System.out.println("name="+e.getName()+",salary="+e.getSalary());
}
}
}
创建一个经理两个雇员,这里需要提到的是e.getSalary(),调用能够确定应该执行哪个getSalary方法,请注意,尽管这里将e声明为Employee类型,但实际上e既可以引用Employee类型的对象,也可以引用Manager类型的对象。
一个对象变量(例如,变量e)可以指示多种实际类型的现象被称为多态(polymorphism)。在运行时能够自动地选择调用哪个方法的现象称为动态绑定(dynamicbinding)。
继承层次
继承并不仅限于一个层次,由一个公共超类派生出来的所有类的集合被称为继承层次(inheritancehierarchy),在继承层次中,从某个特定的类到其祖先的路径被称为该类的继承链(inheritancechain)。通常,一个祖先类可以拥有多个子孙继承链。例如,可以由Employee类派生出子类Programmer或Secretary,它们与Manager类没有任何关系(有可能它们彼此之间也没有任何关系)。
需要注意的是 Java 不支持多继承,但支持多重继承
继承的特性
-
子类拥有父类非 private 的属性、方法。
-
子类可以拥有自己的属性和方法,即子类可以对父类进行扩展。
-
子类可以用自己的方式实现父类的方法。
-
Java 的继承是单继承,但是可以多重继承,单继承就是一个子类只能继承一个父类,多重继承就是,例如 A 类继承 B 类,B 类继承 C 类,所以按照关系就是 C 类是 B 类的父类,B 类是 A 类的父类,这是 Java 继承区别于 C++ 继承的一个特性。
-
提高了类之间的耦合性(继承的缺点,耦合度高就会造成代码之间的联系越紧密,代码独立性越差)。
多态
一个用来判断是否应该设计为继承关系的简单规则,这就是“is-a”规则,它表明子类的每个对象也是超类的对象。is-a”规则的另一种表述法是置换法则。它表明程序中出现超类对象的任何地方都可以用子类对象置换。
例如,可以将一个子类的对象赋给超类变量。
Employee e;
e = new Employee(...);
e = new Manager(...);
在Java程序设计语言中,对象变量是多态的。一个Employee变量既可以引用一个Employee类对象,也可以引用一个Employee类的任何一个子类的对象(例如,Manager、Executive、Secretary等)。然而,不能将一个超类的引用赋给子类变量。
阻止继承:final类和方法
有时候,可能希望阻止人们利用某个类定义子类。不允许扩展的类被称为final类。例如,假设希望阻止人们定义Executive类的子类,就可以在定义这个类的时候使用final修饰符声明。声明格式如下所示:
public final calss Executive extends Manager{
...
}
类中的特定方法也可以被声明为final。如果这样做,子类就不能覆盖这个方法
public class Employee{
...
public final String getName()
}
}
方法或类声明为final主要目的是:确保它们不会在子类中改变语义。