super关键字,是一个隐式参数(另一个隐式参数是this)。
1.概述
super是直接父类的引用(this是当前对象的引用)。可以通过super来访问父类中被子类覆盖的方法或属性。
- super关键字,是一个隐式参数,另外一个隐式参数是this。
super用于方法里面,用于方法外面无意义。 - super关键字一般用于继承和构造方法中。
- 任何类的构造函数中,若是构造函数的第一行代码没有显式的调用super(),那么Java默认都会调用super();作为父类的初始化函数。所以这里的super()加不加都无所谓 。
下面从内存分析的角度去理解super关键字,然后关注其常见用法。
2.super关键字内存分析
2.1分析代码
首先创建一个Bird类,及其直接父类Animal类,并分别创建其构造方法:
public class Animal {
String name;
public Animal() {
}
void eat() {
System.out.println("have a good dinner");
}
}
class Birds extends Animal {
public Birds() {
}
@Override
void eat() {
super.eat(); // 调用父类的方法
System.out.println("----------------------------------------"); // 分隔线
System.out.println("jiujiujiu");
}
void fly() {
System.out.println("Yeah,I can fly~~");
}
}
2.2代码分析
在上述代码中,我们并没有看到super关键字,事实上,在两个构造器中,均有super()
,其实该段代码也可以写成:
public class Animal {
String name;
public Animal() {
super(); // 调用父类Object构造器
}
void eat() {
System.out.println("have a good dinner");
}
}
class Birds extends Animal {
public Birds() {
super(); // 调用父类Animal构造器
}
@Override
void eat() {
super.eat(); // 调用父类的方法
System.out.println("----------------------------------------"); // 分隔线
System.out.println("jiujiujiu");
}
void fly() {
System.out.println("Yeah,I can fly~~");
}
}
可以看出,构造器中通过super()
调用直接父类的构造器。明白了这一点,就可以进行接下来的内存分析。
2.3内存分析
接下来我们进行内存分析,并绘制内存分析图。
首先我们创建一个测试类Test类,并声称一个Birds对象,如下:
class Test {
public static void main(String[] args) {
Birds b = new Birds();
b.eat();
}
}
2.3.2Test类调用main方法
2.3.3Birds b = new Birds();调用Birds构造器
这一步是最重要的,也是重点分析。
1. 当调用Birds类的构造器Birds()
时,里面的第一行代码super()
(无论是否显式写出,都存在),回去寻找Birds类的直接父类,并且Birds构造器停留在此,不会往下执行。
2. 然后找到Animal构造器,并且同样有super()
方法,回去寻找Animal的直接父类,也就是基类Object。
3. 然后执行Object类的构造器,这里Object时所有类的基类,他并没有直接父类。
4. 根据Object类的代码信息,生成第一个Object对象。
5. 此时Animal中的super()
已经执行完成,并接着往下执行,完成了Animal构造器的全部加载,并生成一个Animal对象。
6. 同样的,Birds中的super()
也执行完成。并完成Birds构造器的全部,生成一个Birds对象。
至此,该段代码加载完成。如下图:
b.eat();
eat()
override了父类Animal的方法,我们这里通过b智能调用Birds中的eat()
,但是从图中我们可以看到父类Animal中的eat()
方法仍然存在,那我们怎么使用呢。
答案很简单,我们可以在Birds类中的eat()
中调用父类的该方法,如下:
@Override
void eat() {
super.eat(); // 调用父类的方法
System.out.println("----------------------------------------"); // 分隔线
System.out.println("jiujiujiu");
}
因而,在普通方法中,可以随意通过super调用父类的被override的方法。所以在方法中均存在两个隐式参数,一个是this,一个super。
3.一个有意思的小问题
之前我们提到,super()其实位于构造器的第一行,如果放在其他行会报错,如下:
public Birds() {
System.out.println("sss");
super();
}
报错信息:
Constructor call must be the first statement in a constructor
构造器的调用必须放在构造器的首句。
这句话很熟悉,我们在学习this关键字中也遇到过这句话,我们知道,可以通过this()
调用重载的构造器。那么我们可以同时使用super()和this()吗?
首先我们知道,如果在构造器中使用this(),那么必须具有至少两个构造器。如下:
public Birds() {
super();
}
public Birds(String name) {
this();
}
我们尝试在含参的构造器中使用super():
public Birds(String name) {
this();
super();
}
super()报错:Constructor call must be the first statement in a constructor
换一个顺序:
public Birds(String name) {
super();
this();
}
this()报错:Constructor call must be the first statement in a constructor
无论怎样,两个都必须位于第一行,因而super()和this()不同出现在同一个构造器中。
其实这个情况,也会出现在两个this的情况中,一个Birds(int num,String name)想要同时调用两个构造器Birds()和Birds(String name),也会出现该种情况。如下第二句是会报错的:
public Birds(int num, String name) {
this();
this("niao");
}
Constructor call must be the first statement in a constructor
如果我们想要完成类似的调用,可以这么写:
public Birds() {
super();
}
public Birds(String name) {
this();
}
public Birds(int num, String name) {
this("niao");
}
结论很简单,一个构造器,只能直接调用一个构造器。