63.super关键字
在从子类继承父类的过程中,可能需要在子类中调用父类中的成员,如属性、方法或者构造器,这个时候,可以使用super关键字来完成。
super的作用是用于引用父类的成员,如属性、方法或者是构造器。
调用父类构造器
用于调用父类的构造器,是super的用法之一,它的基本格式是:
super([arg_list])
直接用super()加上父类构造器所需要的参数,就可以调用父类的构造器了。
如果父类中有多个构造器,系统将自动根据super()中的参数个数和参数类型来找出父类中相匹配的构造器。
我们来看下面这个例子,类“Dog”继承了父类“Animal”,并在它的构造器中调用了父类的构造器:
public class Dog extends Animal{
public Dog() {
super(0);
}
//其他代码
… …
}
在Dog这个子类中,有一个构造器Dog(),它里面有一个super(0)的语句,这句话的意思是用参数0来调用父类的构造器,也就是说,如果我们调用Dog的构造器来构建一个对象,它将会去调用父类的构造器来完成这个任务,所以,这个时候,父类中的weight属性的值现在被初始化成0了。
父类必须自己负责初始化它自己的状态而不是让子类来做,因此,如果子类的构造器中没有显式地调用父类构造器,也没有在构造器中调用重载的其它构造器,则系统将会默认调用父类中无参数的构造器。
此时,如果父类中没有无参的构造器,则编译将会出错。
例如,如果将Person类定义成如下:
public class Person {
private String name;
private int age;
private String sex;
public Person(String theName) {
name = theName;
}
public Person(String theName, int theAge, String theSex) {
name = theName;
age = theAge;
sex = theSex;
}
// 其他代码
}
则对于它的子类Teacher来说,如果定义为如下的方式:
public class Teacher extends Person {
private String department;
public void setDepartment(String dept) {
department = dept;
}
public String getDepartment() {
return department;
}
// 其他代码
}
那么,编译这个类的时候,将会出现如下的错误:
Teacher.java:1: cannot resolve symbol
symbol : constructor Person ()
location: class Person
public class Teacher extends Person
^
1 error
这就是因为在调用类Teacher的默认无参数的构造器创建对象的时候,它会去调用父类Person的不带参数构造器,而在类Person中,因为我们没有定义不带参数的构造器,所以,编译器因为无法成功调用Person的不带参数的构造器而报错。
调用父类方法、属性
当super用于引用父类中的属性或方法时,使用下面的形式:
super.属性
super.方法()
例如,我们可以在Teacher这个子类中通过下面的方式来调用父类中方法:
super.getSex();
注意,在这个时候,父类的属性或方法必须是那些protected(受保护)或者public(公共)等可以让子类访问的属性或者方法。
super用于调用父类中的方法主要用于在子类中定义了和父类中同名的属性、或进行了方法的覆盖,而又要在子类中访问父类中的同名属性或覆盖前的方法的时候。
public class Person {
private String name;
private int age;
private String sex;
public String showName() {
return name;
}
// 其他代码
}
在这个例子中,我们假设Person类中的属性都是private的,通过各自的public的方法来存取。
public class Teacher extends Person {
private String department;
public void setDepartment(String dept) {
department = dept;
}
public String getDepartment() {
return department;
}
// 方法覆盖
public String showName() {
// ! return name+"老师";
return super.showName() + "老师";
}
// 测试
public static void main(String[] args) {
Teacher t = new Teacher();
t.setName("Alex");
System.out.println(t.showName());
}
}
在子类Teacher中,如果覆盖方法showName()还是用下面的代码段所示直接返回父类中的属性name,将会因为访问权限问题而出错(private属性只能在本类中被访问):
return name+"老师";
在编译的时候,将会出现下面的错误:
Teacher.java:17: name has private access in Person
return name+"老师";
^
1 error
这个时候,可以在这个覆盖方法中调用被覆盖方法,得到我们需要的“name”属性的值:
public String showName(){
return super.showName()+"老师";
}
这样就解决了访问权限的问题,也清晰的指明了在这个覆盖方法中调用的showName()方法是父类中的方法。
如果不指明此处调用的showName()方法的出处,系统将会当作是类Teacher中的方法,当运行此程序时,将会递归地调用方法本身,引起程序错误。