前言
前些天去面试,做了一套面试题,其中就有个跟构造函数相关的题目,大概是子类是否会输出父类构造器的方法,所以回来后就迫不及待的想验证这一点的实现。
闷头思考便有了以下几个疑问:
- 当一个类没有构造函数时,为什么能成功初始化实例对象?
- 子类能否继承父类的构造函数?
- 子类为什么会隐式调用父类的构造函数?
- 当父类没有无参构造有有参构造时,为什么需要子类显示的调用父类的有参构造函数
构造函数是什么,有何作用
在Java中,构造函数(Constructor)是用于初始化对象的特殊方法,其方法名必须与类名相同,且不能有任何类型的返回类型。
当我们创建一个类的实例化对象(一般是new对象时)时,就会调用构造函数,以此来设置一些对象属性或参数的初始值。
为什么类没有构造函数,照样可以实例化成功呢?
所有的对象都必须有一个默认的构造函数,当然如果你本身没有创建任何构造函数的话,Java其实会在编译源文件的时候,默认创建一个无参且无任何作用的构造函数。如下:
// 源文件
class AClass {
}
// 编译后的class文件
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//
class AClass {
AClass() {
}
}
子类为什么会隐式调用父类的构造函数
在最初学习Java的时候,教科书上就说了构造函数不能被继承(inheritance)。
换个思路,我们继承父类的原因是什么?
就是为了方便对象实例化后,能够使用父类的属性或方法。那么构造函数作为对象初始化的一个方法,在对象被初始化后,那么实例化后的对象就不会再用到构造函数了,因此也没有基础构造函数的必要。
这里就延伸出了另一个问题,既然子类不能继承父类的构造器,那么实例化子类时会先调用子类的构造器进行初始化,而父类又是如何被初始化并被子类使用的呢?
原理就是:子类通过隐式或显示调用父类的无参构造函数来实现父类的初始化。
由于子类的构造函数只能初始化子类的实例变量。为了实现子类对父类方法或属性的调用,当实例化子类对象时,子类对象就必须自动或手动的执行父类的一个构造函数来初始化父类。当然这个构造函数必须根据父类现有的构造函数来实现。
隐式或显示调用的规则
1. 父类没有显式的构造函数时(这时会生成默认构造函数)
- 子类的构造函数默认调用父类的默认构造函数
2. 有无参构造函数以及0个以上的有参构造函数时
- 显示调用:子类的构造函数中使用 super 关键字调用父类的有参或无参构造函数
- 隐式调用:子类不进行显示调用,系统自动调用父类的无参构造函数。
3. 没有无参构造函数,有一个以上的有参构造函数
- 必须在子类的构造器中显式地通过 super 关键字调用父类的构造器,否则会在编译时就报错。
如下的例子:
public class SuperClass {
SuperClass() {
System.out.println("父类无参构造函数------完成初始化");
}
void getMessage() {
System.out.println("父类函数调用");
}
public static void main(String[] args) {
SubClass subClass = new SubClass();
subClass.getMessage();
}
}
class SubClass extends SuperClass {
SubClass() {
System.out.println("子类无参构造函数------完成初始化");
}
}
执行结果:
父类无参构造函数------完成初始化
子类无参构造函数------完成初始化
父类函数调用
Process finished with exit code 0
可以看到,子类初始化时,隐式的调用了父类的无参构造函数。在子类对象实例化的前实现了对父类的实例化,所以我们调用的父类方法也成功了。
参考: