由于子类不能继承父类的构造方法,因此,如果要调用父类的构造方法,可以使用 super 关键字。
super 关键字的功能

  • 在子类的构造方法中显示的调用父类构造方法
  • 访问父类的成员方法和变量

super 调用父类构造方法

super 关键字可以在子类的构造方法中显式地调用父类的构造方法,基本格式如下:super(parameter-list);
其中,parameter-list 指定了父类构造方法中的所有参数。super( ) 必须是在子类构造方法的方法体的第一行。

例1

声明父类 Person 和子类 Student,在 Person 类中定义一个带有参数的构造方法,代码如下:

1
2
3
4
5
6
7
8
public class Person {
public Person(String name) {

}
}
public class Student extends Person {

}

会发现 Student 类出现编译错误,提示必须显式定义构造方法,错误信息如下:

Implicit super constructor Person() is undefined for default constructor. Must define an explicit constructor

IDEA提示错误:There is no default constructor available in 'com.base.learn.Person'

在本例中 JVM 默认给 Student 类加了一个无参构造方法,而在这个方法中默认调用了 super(),但是 Person 类中并不存在该构造方法,所以会编译错误。

如果一个类中没有写任何的构造方法,JVM 会生成一个默认的无参构造方法。在继承关系中,由于在子类的构造方法中,第一条语句默认为调用父类的无参构造方法(即默认为 super(),一般这行代码省略了)。

所以当在父类中定义了有参构造方法,但是没有定义无参构造方法时,编译器会强制要求我们定义一个相同参数类型的构造方法。

例2

声明父类 Person ,类中定义两个构造方法

1
2
3
4
5
6
7
8
9
public class Person {
public Person(String name, int age) {

}

public Person(String name, int age, String sex) {

}
}

子类 Student 继承了 Person 类,使用 super 语句来定义 Student 类的构造方法。示例代码如下:

1
2
3
4
5
6
7
8
9
10
public class Student extends Person {
public Student(String name, int age) {
super(name, age); // 调用父类含有2个参数的构造方法
}

public Student(String name, int age, String sex) {
super(name, age, sex); // 调用父类中含有3个参数的构造方法
}

}

从上述 Student 类构造方法代码可以看出,super 可以用来直接调用父类中的构造方法,使编写代码也更加简洁方便。
编译器会自动在子类构造方法的第一句加上 super() 来调用父类的无参构造方法,必须写在子类构造方法的第一句,也可以省略不写。通过 super 来调用父类其它构造方法时,只需要把相应的参数传过去。

super 访问父类成员

当子类的成员变量或方法与父类同名时,可以使用 super 关键字来访问。如果子类重写了父类的某一个方法,即子类和父类有相同的方法定义,但是有不同的方法体,此时,可以通过 super 来调用父类里面的这个方法。

使用 super 访问父类中的属性和方法 与 this 关键字的使用类似 super.member

其中,member 是父类中的属性或方法。使用 super 访问父类的属性和方法时不用位于第一行。

super 调用成员属性

当父类和子类具有相同的数据成员时,可以使用 super 关键字访问父类中的属性

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
class Person {
int age = 12;
}

class Student extends Person {
int age = 18;

void display(){
System.out.println("学生的年龄:" + super.age);
}
}

class Test {
public static void main(String[] args) {
Student stu = new Student();
stu.display();
}
}
// 学生年龄:12

super 调用成员方法

当父类和子类都具有相同方法名时,可以使用 super 关键字访问父类的方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
class Person {

void message() {
System.out.println("This is person class.");
}
}

class Student extends Person {

void message() {
System.out.println("This is student class.");
}

void display(){
message();
super.message();
}
}

class Test {
public static void main(String[] args) {
Student stu = new Student();
stu.display();
}
}

// This is student class.
// This is person class.

super 和 this 的区别

this 指的是当前对象的引用,super 是当前对象父对象的引用

super 关键字的用法

  • super.父类属性名:调用父类中的属性
  • super.父类方法名:调用父类中的方法
  • super():调用父类的无参构造方法
  • super(参数):调用父类的有参构造方法

如果构造方法的第一行代码不是 this() 和 super(),则系统会默认添加 super()。

this 关键字的用法:

  • this.属性名:表示当前对象的属性
  • this.方法名(参数):表示调用当前对象的方法

当局部变量和成员变量发生冲突时,使用 this.进行区分。

子类对象实例化全过程

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
class Creature{ //生物类
//声明属性、方法、构造器
}

class Animal extends Creature{ //动物类

}

class Dog extends Animal{ //狗类

}

class DogTest{
public static void main(String[] args){
Dog dog = new Dog();
dog.xxx();
dog.yyy = ...;
}
}

从结果的角度来看:体现为类的继承性

  • 当我们创建子类对象后,子类对象就获取了其父类中声明的所有的属性和方法,在权限允许的情况下,可以直接调用。

从过程的角度来看:

  • 当我们通过子类的构造器创建对象时,子类的构造器一定会直接或间接的调用到其父类的构造器,而其父类的构造器,同样会直接或间接的调用到其父类的父类的构造器,….,直到调用了Object类中的构造器为止。

  • 正因为我们调用过子类所有的父类的构造器,所以我们就会将父类中声明的属性、方法加载到内存中,供子类的对象使用。

问题1:在创建子类对象的过程中,一定会调用父类中的构造器吗? yes!

问题2:创建子类的对象时,内存中到底有几个对象?

  • 就只有一个对象!即为当前new后面构造器对应的类的对象。