我们在构造方法的继承、调用顺序(一)一文中,已经了解过子类的构造过程中,构造方法是如何调用的。
那么,我们看下面一个例子来巩固一下:
package com.company;
public class Main {
public static void main(String[] args) {
new B(6); //B类型实例化,会调用到B类型的构造函数
}
public static class A {
private int f1 = 7; //(1)
public A(int b) { //(A())
this.f1 = b; //(2)
System.out.println("step1"); //(3)
initialize(); //(4)
}
protected void initialize() { //(5)
System.out.println("step2");
System.out.println(f1);
}
}
public static class B extends A { // B是A的子类,所以才能在B的构造方法中调用A的构造方法
protected int f1 = 3; //(6)
public B(int f1) { //B类型实例化,会调用到B类型的构造方法
super(f1); //(7)
this.f1 += f1; //(8)
initialize(); //(9)
}
protected void initialize() { //(10)
System.out.println("step3");
System.out.println(f1);
}
}
}
输出:
step1
step3
0
step3
9
在上面的代码中,new B(6) 对子类B进行实例化,
new B(6) ---->super(6) ,但是这个时候并没有对B类的成员B.f1初始化,
这是为什么呢?
这是因为B的父类A还未完成构造,jvm会先对父类A进行构造,包括对A.f1初始化 —> A,构造()两个步骤,
在A的构造方法中,调用了initialize(),这个initialize是 B.initialize(),
这是因为A.initialize在此处被B.initialize()所重写,如果将A.initialize修改为private,那么这个重写将不成功(编译不会报错)。
那么这个调用顺序如下;
new B(6) ——> (7)super(f1)检查到B有父类A,进入父类A的构造过程 ——>
——> (1) A.f1初始化 ——> A.A() ——>{(2)(3)(4)——>(10) --(7)完成}
——> 进入子类B的构造过程 ——>
——>(6) B.f1初始化 ——>(8) (此刻B.f1已被初始化) ——>(9)——>(10)。
A.initialize被重写,会导致调用顺序发生变化,为了验证这个问题,我们将上面A类中的代码修改一下:
public static void initialize() { //(5)
编译报错:
Error:(33, 24) java: com.company.Main.B中的initialize()无法覆盖com.company.Main.A中的initialize()
被覆盖的方法为static
我们将代码再次修改,验证:
修改A类中的代码回原样:
protected void initialize() { //(5)
修改B类中的代码:
private void initialize() { //(10)
再次验证,发现(4)处调用的是A.initialize(),而不是B.initialize,
这是因为B虽然对A的initialize进行了重写,但是由于B中initialize使用了private限定,导致在A类中无法访问B.initialize,最后jvm仍然调用了A的initialize。