目录
继承是一种由已有的类创建新类的机制。利用继承,我们可以先创建一个共有属性的一般类,再根据该一般类创建具有特殊属性的新类,新类继承一般类的状态和行为,并根据需要增加自己新的状态和行为。由继承得到的类称为子类或派生类,被继承的类称为父类、基类或超类。Java 不支持多重继承,子类只能有一个父类。
先看以下代码:
public class Saloon_car {
String engine; //引擎
String Wheel; //车轮
String airbag; //货舱
public void run(){ //方法
//定义车跑动的行为
}
}
public class Truck {
String engine; //引擎
String Wheel; //车轮
String carport; //安全气囊
public void run(){ //方法
//定义车跑动的行为
}
}
上面写了两个类的定义,我们会发现,这两个类有很多重复的属性和行为,这不仅带来了代码的繁杂,产生大量冗余代码,而且更重要的是,程序变得难以驾驭,我们很难对程序进行更新。
如果现在我们要在创建一个或多个类属性和行为相似的类,就要单独写多个重复的属性和行为如果这样的话会造成代码冗余。
那么如何解决这样的问题,就是要使用继承
继承使用的关键字是extends
首先可以写一个父类——Car,在定义将车,卡车类,它们拥有父类Car的所有特征,再加上自己独有的特性,形成Saloon_car,Truck.代码如下
//汽车类
public class Car {
String engine; //引擎
String Wheel; //车轮
//....其他属性
public void run(){ //方法
//定义车跑动的行为
System.out.println("汽车在奔跑!");
}
}
//轿车类
public class Saloon_car extends Car{
String airbag; //安全气囊
}
//卡车类
public class Saloon_car extends Car{
String carport; //货舱
}
在上面代码,轿车类和卡车类继承后,拥有了汽车类中定义的所有属性和方法。我们可以测试一下
运行如下
方法的覆盖
观察上图可以发现,不管是普通汽车,还是卡车和轿车,它们的运转都是一样的,结果均输出“汽车在奔跑!”,这是不恰当的。按道理来说,卡车和轿车的运转应该有自己的独立方式,不应当和普通汽车保持一致。也就是说,子类需要对父类的 run(方法加以改进,变成子类自己的 run()方法,这就需要在子类中重新编写 run()方法,覆盖父类的 run()方法,这种做法在 Java 中叫作方法的覆盖(overide),又称方法重写。
实例如下
//父类汽车类
public class Car {
String engine; // 引擎
String Wheel; // 车轮
// ....其他属性
public void run() { // 方法
// 定义车跑动的行为
System.out.println("汽车在奔跑!");
}
}
//子类轿车类
public class Saloon_car extends Car {
String airbag; //安全气囊
public void run() { //不用父类方法,重新编写的run方法
//定义轿车
System.out.println("轿车在高速路上奔驰!");
}
}
//子类卡车类
public class Truck extends Car {
String carport; // 货舱
public void run() { // 不用父类方法,重新编写的run方法
System.out.println("卡车在工地上忙碌!");
}
}
再次运行程序,这时结果就正常了,汽车、轿车、卡车分别以自己的方式在奔跑。方法覆盖要求:子类方法和父类方法同名,且参数相同。
要注意重载和重写的区别,重载既可以发生于一个类,也可以发生于子类与父类之间(子类继承父类方法,同时完成方法重载),而重写,只能是子类重写父类方法。
super关键字
学完继承后,我们注意到一个问题,在某些时候,子类需要调用父类的某些方法,如以下代码
public class Calculator {
public void play() {
System.out.println("完成四则运算功能");
this.print(); //引用print()方法
}
public void print() {
System.out.println("显示结果!");
}
}
public class Computer extends Calculator {
public void play() {
System.out.println("完成四则运算功能");
this.print(); //引用print()方法
System.out.println("完成娱乐功能");
this.print(); //引用print()方法
}
public static void main(String[] args) {
Computer c = new Computer(); //实例化对象
c.play(); //引用play()方法
}
}
在 Calculator(计算器)类中,定义了 play)方法实现四则运算功能。子类 Computer(计算
了,我们知道,不管是计算器,还是计算机,它们在四则运算功能上是一样的,所以没必
要让计算机类重新实现一遍,完全可以直接使用父类已完成的功能,这样不仅使代码简洁,
更重要的是便于程序的更新。
public class Computer extends Calculator {
public void computerCalc() {
this.play(); //调用play()方法
System.out.println("完成娱乐功能!");
this.print(); //调用print()方法
}
}
这种做法是在 Computer 类内重新定义一个新方法 computerCalc(),因为 Computer类
继承了 Calculator 类,所以可以直接在其中调用 play0方法。
虽然这种做法没有任何问题,但是不便于程序的阅读。若想在重写的同时能调用父类
的对应方法,该如何实现呢?答案就是 super 关键字。
super关键字引用当前对象的直接父类中的属性和方法(用来访问直接父类中被隐藏的
属性和方法,经常在基类与派生类中有相同属性和方法定义时使用)。
public class Computer extends Calculator {
public void play() {
//System.out.println("完成四则运算功能");
//this.print();
super.play(); //调用父类的同名play()方法
System.out.println("完成娱乐功能");
this.print();
}
public static void main(String[] args) {
Computer c= new Computer();
c.play();
}
}
运行程序,发现结果与修改前是一样的,可见 super 关键字的功用绝非一般。
除了调用父类同名方法外,super 还广泛地运用于构造方法内,如下列代码
//Calculator类
public class Calculator {
Calculator(){ //方法
System.out.println("产生计算机!");
}
}
//Computer类
public class Computer extends Calculator {
Computer(){ //方法
System.out.println("产生计算机!");
}
}
//测试类
public class Test {
public static void main(String[] args) {
Computer comp = new Computer(); //对象实例化
}
}
运行结果如下
匪夷所思,在调用计算机类构造方法的同时,却也调用了计算器类的构造方法,其实,
这也是 super 关键字的原因。在子类的构造方法内,存在一个 super(用于调用父类的默认
构造方法。其实 Computer 类的完整写法如下
public class Computer extends Calculator {
Computer() {
super(); // 由系统自动生成,代表父类默认的构造方法
System.out.println("产生计算机!");
}
}
发挥一下想象力,既然系统自动生成了 super()方法,那我们能不能自己自由书写 superl
呢?答案是肯定的,只不过要遵循如下原则:
- super 要放在第一行。
- super 所指引的构造方法在父类中必须要有。
- 如下代码
public class Employee {
String name; //属性
public Employee(String name) {
this.name = name; //实参name赋值给当前类的name
}
}
public class Manager extends Employee {
String department; //属性
public Manager(String name,String department) {
super(name); //获取父类的name属性
this.department = department; //将参数的值赋值给当前类的department属性
}
}
在 Manager 类中,不需要再次书写“this.name = name”,而只需要把 name 参数通过
super()方法传递给父类的对应构造方法,由父类完成该参数的初始化即可。