看如下程序代码段:
public class Test1 {
public static void main(String[] args) {
A b = new B();
b.print();
}
}
class A {
public A(){
print();
}
public void print(){
System.out.println("A");
}
}
class B extends A{
int i = 1;
public B(){
print();
}
public void print(){
System.out.println("B" + i);
}
}
程序运行结果如下:
运行过程解释:
在构造B的时候,会先构造A,在A的构造函数中调用了print()方法,print()方法在B中被重写了,A的构造函数中实际上调用的是B中重写的print()方法,但输出为什么是B0,而不是B1呢?这是因为此时,A还没有构造完成,B更加没有构造完成,变量i是B中的成员变量,此时变量i还没有被初始化,所以其值为默认值0,所以打印出B0。
之后的就好理解了,构造B时,先会初始化成员变量i,i=1,之后执行构造函数,打印出B1。
后面的,根据多态,打印出B1,没有问题。
为了避免父类的构造函数调用子类的多态方法,一般可以将父类的同名函数申明为private,这样在父类构造函数中调用时就是调用的自己的方法。
有一点要注意的是,此时b.print()不能通过编译(图中将其注释了),这是因为b是A类型的引用,print在A中是private的,无法访问。如果将b改为B类型的(B b = new B())则可以了。
总结:
如果要在构造器中调用一个方法时,将该方法声明为private。
假使你的父类构造器中要调用一个非静态方法,而这个方法不是private的又被子类所重载,这样在实际创建子类的过程中递归调用到了父类的构造器时,父类构造器对这个方法的调用就会由于多态而实际上调用了子类的方法,当这个子类方法需要用到子类中实例变量的时候,就会由于变量没有初始化而出现异常(至于为什么子类中的实例变量没有初始化可以参考上边的实例初始化过程),这是Java不想看到的情况。而当父类构造器中调用的方法是一个private方法时,多态就不会出现,也就不会出现父类构造器调用子类方法的情况,这样可以保证父类始终调用自己的方法,即使这个方法中调用了父类中的实例变量也不会出现变量未初始化的情况(变量初始化总是在当前类构造器主体执行之前进行)。