在基类的构造器中要尽量避免调用可能被继承重写的方法,否则在初始化时将会引发意外
一个动态绑定的方法调用会深入到继承层次的结构中,它可以调用导出类中的方法,如果在基类的构造器中这样做,就可能会调用某个方法,而这个方法所操纵的成员可能还未进行初始化:
class Glyph{
void draw(){
print("Glyph.draw()"); //这个方法其实并未被调用
}
Glyph{
print("Glyph() bfore draw()");
draw(); //就是在这个地方,调用了可能在导出类中重写的方法,将可能导致意外结果
print("Glyph() after draw()");
}
}
class RoundGlyph extends Glyph{
private int radius = 1;
RoundGlyph(int r){
radius = r;
print("RoundGlyph.RounGlyph(), radius=" + radius);
}
void draw(){
print("RoundGlyph.draw(), radius = "+ radius); //基类在构造时实际调用的是此方法
}
}
public class Test(){
public static void main(String[] args){
new RoundGlyph(5); //初始化过程会先调用基类的构造器,基类初始化完成后才会进行导出类的初始化
}
}
/*
output:
Glyph() before draw()
RoundGlyph.draw(), radius = 0 //调用了导出类中的方法,但相关参数在子类中,还未初始化
Glyph() after draw()
RoundGlyph.RounGlyph(), radius= 5 //最后才是进行导出类的初始化
*/
在使用继承以及多态时的初始化过程:
1)在任何事件发生之前,将分配给对象的存储空间初始化成二进制的零
2)调用基类构造器。如果此时调用了被重写的方法,用于步骤一的缘故,方法中使用的变量值为0
3)按照声明的顺序调用成员的初始化方法
4)调用导出类的构造器主体
注: 若导出类中的方法通过“组合”方式嵌入了一个类内部的对象的引用,其值就是null
总结:
在编写构造器时,需要尽可能用简单的方法使对象进入正常状态,尽量避免调用其他方法。
在构造器中能够安全调用的只有基类中的final方法(也适用于private方法,它们自动属于final方法)。这些方法不能被覆盖,因此才是安全的。