super使用在构造方法中,语法格式为:super(实际参数列表),这行代码和“this(实际参数列表)”都是只允许出现在构造方法第一行(这一点记住就行了),所以这两行代码是无法共存的。“super(实际参数列表)”这种语法表示子类构造方法执行过程中调用父类的构造方法。我们来看一段代码:
public class People {
String idCard;
String name;
boolean sex;
public People(){
}
public People(String idCard,String name,boolean sex){
this.idCard = idCard;
this.name = name;
this.sex = sex;
}
}
public class Student extends People{
//学号是子类特有的
int sno;
public Student(){
}
public Student(String idCard,String name,boolean sex,int sno){
this.idCard = idCard;
this.name = name;
this.sex = sex;
this.sno = sno;
}
}
public class StudentTest {
public static void main(String[] args) {
Student s = new Student("12345x","jack",true,100);
System.out.println("身份证号" + s.idCard);
System.out.println("姓名" + s.name);
System.out.println("性别" + s.sex);
System.out.println("学号" + s.sno);
}
}
运行结果如下图所示:
图14-4:运行结果
我们把上面的代码片段拿过来放在一起看看:
父类的构造方法:
public People(String idCard,String name,boolean sex){
this.idCard = idCard;
this.name = name;
this.sex = sex;
}
子类的构造方法:
public Student(String idCard,String name,boolean sex,int sno){
this.idCard = idCard;
this.name = name;
this.sex = sex;
this.sno = sno;
你有没有察觉到子类的构造方法前三行代码和父类构造方法中的代码是一样的?接下来把子类的构造方法修改一下,然后再运行测试程序:
public Student(String idCard,String name,boolean sex,int sno){
super(idCard,name,sex);
this.sno = sno;
}
运行结果如下图所示:
图14-5:运行结果
通过以上代码的学习,“super(实际参数列表);”语法表示调用父类的构造方法,代码复用性增强了,另外一方面也是模拟现实世界当中的“要想有儿子,必须先有父亲”的道理。不过这里的“super(实际参数列表)”在调用父类构造方法的时候,从本质上来说并不是创建一个“独立的父类对象”,而是为了完成当前对象的父类型特征的初始化操作。(或者说通过子类的构造方法调用父类的构造方法,是为了让张小明身上长出具有他父亲特点的鼻子和眼睛,鼻子和眼睛初始化完毕之后,具有父亲的特点,但最终还是长在张小明的身上)。
接下来,再来看一段代码:
public class A {
public A(){
System.out.println("A类的无参数构造方法执行");
}
}
public class B extends A {
public B(){
System.out.println("B类的无参数构造方法执行");
}
}
public class C extends B {
public C(){
System.out.println("C类的无参数构造方法执行");
}
}
public class Test {
public static void main(String[] args) {
new C();
}
}
运行结果如下图所示:
图14-6:super()的测试
通过以上运行结果可以得出以下的等效代码:
public class A {
public A(){
//这里调用的是Object类中的无参数构造方法
//因为A类的父类是Object
super();
System.out.println("A类的无参数构造方法执行");
}
}
public class B extends A {
public B(){
super();
System.out.println("B类的无参数构造方法执行");
}
}
public class C extends B {
public C(){
super();
System.out.println("C类的无参数构造方法执行");
}
}
运行结果如下图所示:
图14-7:super()的测试
通过以上代码的测试我们得出,当一个构造方法第一行没有显示的调用“super(实际参数列表)”的话,系统默认调用父类的无参数构造方法“super()”。当然前提是“this(实际参数列表)”也没有显示的去调用(因为super()和this()都只能出现在构造方法第一行,所以不能并存)。我们可以通过以下程序再次测试一下:
public class A {
//有参数构造方法定义之后
//系统则不再提供无参数构造方法
public A(String s){
}
}
public class B extends A {
public B(){
}
}
编译报错了:
图14-8:编译报错信息
以上程序为什么会编译报错呢?原因是B类的构造方法第一行默认会调用“super()”,而super()会调用父类A的无参数构造方法,但由于父类A中提供了有参数构造方法,导致无参数构造方法不存在,从而编译报错了。所以在实际开发中还是建议程序员将无参数构造方法显示的定义出来,这样就可以避免对象的创建失败了。
另外,通过以上内容的学习,还可以得出这样的结论:在java语言当中无论是创建哪个java对象,老祖宗Object类中的无参数构造方法是必然执行的。
接下来我们再来看一下:一个java对象在创建过程中比较完整的内存图是如何变化的,请先看以下代码:
public class People {
String name;
boolean sex;
public People(String name,boolean sex){
this.name = name;
this.sex = sex;
}
}
public class Worker extends People {
//子类特有的工资属性
double salary;
public Worker(String name,boolean sex,double salary){
super(name,sex);
this.salary = salary;
}
}
public class WorkerTest {
public static void main(String[] args) {
Worker w = new Worker("jack",true,10000.0);
System.out.println("姓名:" + w.name);
System.out.println("性别:" + w.sex);
System.out.println("工资:" + w.salary);
}
}
运行结果如下图所示:
图14-9:运行结果
以上程序创建Worker对象时构造方法的执行顺序是:
● 先执行Object类的无参数构造方法;
● 再执行People类的构造方法;
● 最后执行Worker类的构造方法;
注意:虽然执行了三个构造方法,但是对象实际上只创建了一个Worker。
以上程序的内存结构图是这样变化的:
图14-10:程序执行内存图
通过以上内容的学习,super()的作用主要是:第一,调用父类的构造方法,使用这个构造方法来给当前子类对象初始化父类型特征;第二,代码复用。