/**
* @Description 构造器中的多态
* @Date 2021-01-13 10:06
*/
class Glyph{
void draw(){
System.out.println("Glyph draw");
}
Glyph(){
System.out.println("Glyph before draw");
draw();
System.out.println("Glyph after draw");
}
}
public class RoundGlyph extends Glyph{
private int radius = 1;
RoundGlyph(int r){
radius = r;
System.out.println("RoundGlyph.radius "+radius);
}
@Override
void draw() {
System.out.println("RoundGlyph.draw.radius "+radius);
}
public static void main(String[] args) {
new RoundGlyph(5);
//初始化顺序:
// RoundGlyph构造器,发现有父类,找到Glyph构造器
// 输出 Glyph before draw
// Glyph.draw()方法,多态,找到RoundGlyph.draw(),但是这个时候RoundGlyph的radius值没被初始化,是最初的默认值(二进制的零),int型为0,输出 RoundGlyph.draw.radius 0
// 输出 Glyph after draw (父类初始化完毕)
// 回到RoundGlyph,radius初始化为1,5被传入,radius=5,输出 RoundGlyph.radius 5
}
}
Glyph.draw()方法设计为将要被覆盖,这种覆盖是在RoundGlyph中发生的。但是Glyph构造器会调用这个方法,结果导致了对RoundGlyph.draw()的调用,这看起来似乎是我们的目的。但是如果看到输出结果,我们会发现当Glyph的构造器调用draw()方法时,radius不是默认初始值1,而是0。
初始化的实际过程:
1)在其他任何事物发生之前,将分配给对象的存储空间初始化成二进制的零。
2)如前述那样调用基类构造器。此时,调用被覆盖后的draw()方法(要在调用RoundGlyph构造器之前调用),由于步骤1的缘故,我们此时会发现radius的值为0.
3)按照声明的顺序调用成员的初始化方法。
4)调用导出类的构造器主体。
这样做有一个优点,那就是所有东西都至少初始化成零(或者是某些特殊数据类型中与"零"等价的值),而不是仅仅留作垃圾。包括通过"组合"而嵌入一个类内部的对象引用,其值是null。所以如果忘记为该引用进行初始化,就会在运行时出现异常。
因此,编写构造器时有一条有效的准则:"用尽可能简单的方法使对象进入正常状态;如果可以的话,避免调用其他方法"。在构造器内唯一能够安全调用的那些方法使基类中的final方法(private方法都隐式地指定为是final)。这些方法不能被覆盖,因此也不会出现上述令人惊讶的问题。