如果在构造函数中使用super调用重写方法会发生什么

Rou*_*han 7 java constructor super

有两个类Super1Sub1

Super1.class

public class Super1 {
    Super1 (){
        this.printThree();
    }

    public void printThree(){
        System.out.println("Print Three");
    }    
}
Run Code Online (Sandbox Code Playgroud)

Sub1.class

public class Sub1 extends Super1 {
    Sub1 (){
        super.printThree();
    }

    int three=(int) Math.PI;

    public void printThree(){
        System.out.println(three);
    }

    public static void main(String ...a){
         new Sub1().printThree();
    }
}
Run Code Online (Sandbox Code Playgroud)

当我调用printThree类的方法时,Sub1我希望输出为:

打印三
3

因为Sub1构造函数调用了super.printThree();.

但我真的得到了

0
打印3
3

我知道0是默认值,int但它是如何发生的?

T.J*_*der 15

你看到了三件事的影响:

  1. 默认超级构造函数调用,和

  2. 实例初始化程序相对于超级调用,和

  3. 被覆盖的方法如何工作

你的Sub1构造是真的这样的:

Sub1(){
    super();               // <== Default super() call, inserted by the compiler
    three=(int) Math.PI;   // <== Instance initializers are really inserted
                           // into constructors by the compiler
    super.printThree();
}
Run Code Online (Sandbox Code Playgroud)

(令人惊讶,我知道,但这是真的.用javap -c YourClass来看.:-))

看起来这样的原因是超类必须有机会在子类初始化对象的一部分之前初始化对象的一部分.所以你得到了这种交织效果.

鉴于这就是Sub1 真实的样子,让我们来看看:

  1. JVM创建实例并将所有实例字段设置为其默认值(所有位都关闭).所以在这一点上,该three领域存在,并具有价值0.

  2. JVM调用Sub1.

  3. Sub1立即调用super()(Super1),...

    1. ......打电话printThree.由于printThree被覆盖,即使对它的调用是在代码中Super1,它也是调用的重写方法(in in Sub1).这是Java实现多态的一部分.由于three实例初始化程序尚未运行,three包含0,这就是输出的内容.

    2. Super1 回报.

  4. 回来Sub1,three编译器插入(重新定位,真正)的实例初始化代码运行并给出three一个新值.

  5. Sub1电话printThree.由于three实例初始化程序代码现在已经运行,因此printThree打印3.

关于此实例初始化程序代码被移入构造函数,您可能想知道:如果我有多个构造函数怎么办?代码被移入哪一个?答案是编译器将代码复制到每个构造函数中.(你也可以看到它javap -c.)(如果你有一个非常复杂的实例初始化器,如果编译器有效地将它变成一个方法,我不会感到惊讶,但我还没看过.)

如果你做一些非常顽皮的事情并在你的实例初始化期间调用一个方法,那就更清楚了一点:( 实时拷贝)

class Super
{
    public static void main (String[] args) {
        new Sub();
    }

    Super() {
        System.out.println("Super constructor");
        this.printThree();
    }

    protected void printThree() {
        System.out.println("Super's printThree");
    }
}
class Sub extends Super
{
    int three = this.initThree();

    Sub() {
        this.printThree();
    }

    private int initThree() {
        System.out.println("Sub's initThree");
        return 3;
    }

    protected void printThree() {
        System.out.println("Sub's printThree: " + this.three);
    }
}
Run Code Online (Sandbox Code Playgroud)

输出:

Super constructor
Sub's printThree: 0
Sub's initThree
Sub's printThree: 3

注意"Sub的initThree"在那个序列中出现的位置.