我们在构造方法的继承、调用顺序(一)一文中,已经了解过子类的构造过程中,构造方法是如何调用的。
那么,我们看下面一个例子来巩固一下:
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。
