先看代码
class Parent {
Parent() {
System.out.println("开始父类的无参构造");
draw();
System.out.println("结束父类的无参构造");
}
void draw() {
System.out.println("父类的 draw 方法");
}
}
class Child extends Parent {
private int radius = 1;
Child(int r) {
System.out.println("开始子类的构造");
radius = r;
System.out.println("结束调用子类的构造, radius=" + radius);
}
void draw() {
System.out.println("调用子类的 draw 方法,radius=" + radius);
}
public static void main(String[] args) {
new Child(100);
}
}
==========控制台==========
开始父类的无参构造
调用子类的 draw 方法,radius=0
结束父类的无参构造
开始子类的构造
结束调用子类的构造, radius=100
疑问1:
为什么在构造方法中可以调用到该类的普通方法?不应该是构造函数执行完毕后该对象才算创建完成,才能使用其属于对象的普通方法吗?
我的猜想:
对象的创建完成,包括两部分,一是该对象的类已经加载进内存,二是数据已经初始化完成。其实当执行构造方法时,说明该类已经加载完毕(类只加载一次),此时,在构造方法中仅仅是做一些显示的初始化而已。当在构造方法中调用普通方法时,由于该数据已经加载进了内存,所以是可以调用的,只是还未做显示的数据初始化而已。
疑问2:
为什么在继承树追溯的过程中,父类构造方法中调用了子类重写过的方法,会执行子类重写的方法,而不是父类自己的方法,此时子类对象还并未创建啊?(注意,这里说的是继承树追溯的过程中,不是指子类已经创建完毕,因向上转型而出现的多态的特性)
我的猜想:
如果我疑问1的猜想成立,那么这个就很好解释了。首先,程序运行时类已经加载进了内存,只要加载进内存的数据,符合访问控制权限,就可以访问到,而构造方法只是对其做显示的初始化而已。
继承树追溯的过程,就是把父类对象中非私有数据(非private修饰)+ 非构造方法 的所有数据复制给子类对象而已,这样就达到了代码复用的目的。即,继承树追溯的过程,就是将父类的数据复制给子类对象的过程。因为整个追溯过程都是为了创建子类对象,所以当父类构造方法中调用被子类重写过的方法时,理应体现出子类的特性。
拓展:
如果父类对象的构造方法中调用了子类重写的方法,由上面的分析可知,会执行子类中的该方法,但是,此时的子类还未显式初始化,成员变量都是默认初始化值。那么子类重写的该方法中使用了子类的成员变量的某些方法,就会报空指针异常。