为什么要继承
继承就是子类继承父类的变量和函数,通过继承,可以使代码更加简洁,可维护性更强。
比如当我们想编写的两个类拥有很多相似的变量和函数时,我们可以写一个父类,其中包含这两个子类共有的变量与函数,两个子类继承这个父类之后就可以直接调用这些变量与函数,同时也可以在子类里面定义该子类特有的变量与函数。既简化了代码,又使得维护代码时需要修改的部分减少了,提高了可维护性。
继承的特点
子类拥有父类非 private 的属性、方法。
子类可以拥有自己的属性和方法,即子类可以对父类进行扩展。
子类可以用自己的方式实现父类的方法。
Java 的继承是单继承,但是可以多重继承,单继承就是一个子类只能继承一个父类,多重继承就是,例如 B 类继承 A 类,C 类继承 B 类,所以按照关系就是 B 类是 C 类的父类,A 类是 B 类的父类,这是 Java 继承区别于 C++ 继承的一个特性。
提高了类之间的耦合性(继承的缺点,耦合度高就会造成代码之间的联系越紧密,代码独立性越差)。
继承的实现
extends
一般通过extends
实现继承,该关键字为单一继承(不能一个类继承多个类),如
class A extends B{
}
就是A类继承B类。
还有一种继承实现并不常用,为implements
,使用范围为类继承接口的情况,暂时略过不提。
下面给出一段有关继承的代码,也是学生管理系统,其中CSStu(计算机学生)类继承Student(学生)类。在后面的讲解中也将用到这段代码。
package test;
class Student{//父类
String name;
int age;
double grade;
public Student(){//无参数
System.out.println("父类的无参数构造器被调用");
}
public Student(String name,int age,double grade) {//有参数
System.out.println("父类的有参数构造器被调用");
this.name=name;
this.age=age;
this.grade=grade;
}
public void Stuprint() {
System.out.println("name:"+name);
System.out.println("age:"+age);
System.out.println("grade:"+grade);
}
}
class CSStu extends Student{//子类
public CSStu(){//无参数
System.out.println("子类的无参数构造器被调用");
}
public CSStu(String name,int age,double grade) {//有参数
super(name,age,grade);
System.out.println("子类的有参数构造器被调用");
}
public void CSprint() {
System.out.println("This is a CS Student.");
super.Stuprint();
}
}
public class Inheritance {
public static void main(String[] args) {
CSStu ShadyPi=new CSStu();//调用无参数构造器定义一个空CSStu对象
ShadyPi.Stuprint();
CSStu Pi=new CSStu("ShadyPi",19,0.1);
Pi.Stuprint();
Pi.CSprint();
}
}
这段代码的运行结果如下:
父类的无参数构造器被调用
子类的无参数构造器被调用
name:null
age:0
grade:0.0
父类的有参数构造器被调用
子类的有参数构造器被调用
name:ShadyPi
age:19
grade:0.1
This is a CS Student.
name:ShadyPi
age:19
grade:0.1
super和this
顾名思义,super
指向当前对象的父类,而this
指向自己,一般用在类的声明里。
可以看到,在定义函数CSprint
时,我们直接用super
调用了父类的Stuprint
函数,同时还可以加入别的操作,使得编程更简洁高效。
public void CSprint() {
System.out.println("This is a CS Student.");
super.Stuprint();
}
构造器
可以看到,在定义父类Student
和子类CSStu
时,我们分别写了两个构造器,一个是有参数的,一个是无参数的。
子类是不继承父类的构造器(构造方法或者构造函数)的,它只是调用(隐式或显式)。如果父类的构造器带有参数,则必须在子类的构造器中显式地通过 super
关键字调用父类的构造器并配以适当的参数列表。
如果父类构造器没有参数,则在子类的构造器中不需要使用 super
关键字调用父类构造器,系统会自动调用父类的无参构造器。
需要注意的是,若要调用有参数的父类构造器,构造语句需要写在第一行,否则会CE。
public CSStu(String name,int age,double grade) {//有参数
super(name,age,grade);
System.out.println("子类的有参数构造器被调用");
}
从输出也可以看到函数的调用顺序,可以发现无论有参数还是无参数构造器,进入子类构造器后第一个触发的就是父类构造器。
父类的无参数构造器被调用
子类的无参数构造器被调用
name:null
age:0
grade:0.0
父类的有参数构造器被调用
子类的有参数构造器被调用
其他函数
前面说到,子类会继承父类的所有变量与函数,所以子类除了在定义过程中通过super
调用,还可以直接在外部直接调用,如
public static void main(String[] args) {
CSStu ShadyPi=new CSStu();
ShadyPi.Stuprint();
CSStu Pi=new CSStu("ShadyPi",19,0.1);
Pi.Stuprint();
Pi.CSprint();
}
CSStu
类的对象Pi
仍然可以直接调用定义在Student
类的Stuprint
函数。