构造函数 - 执行顺序


class Alpha
{
       String name = "Alpha";
       Alpha()
       {
           print();
       }
       void print()
       {
           System.out.println("Alpha Constructor");
       }
}
class Beta extends Alpha
{
       int i =   5;
       String name = "Beta";
       public static void main(String[] args)
       {
          Alpha a = new Beta();
          a.print();//Line1 executes Beta constructor
          System.out.println(a.name);//Line 2 displays Alpha instance variable
       }
       void print()
       {
           System.out.println(i);
       }
}

此程序编译成功并显示以下输出。

0
5
Alpha

问题

a( 我不明白为什么 Alpha 的构造函数没有首先被执行。

我相信"super(("首先会被每个子构造函数隐式调用......右?。

b( 如果 Beta 的构造函数已经执行,那么为什么要打印"5"?(输出中的第二行(

第三行我有点理解(即将显示 Alpha 自己的变量,因为尚未在"一个"实例变量上完成转换(

你在这里犯了两个"重罪":

  1. 从构造函数调用可重写的方法;
  2. 声明子类中的实例变量与
  3. 超类中的实例变量同名。

这两个习语都会导致令人惊讶的行为。具体来说,1.导致Beta#printAlpha的构造函数中调用,这会导致打印0,因为您正在Beta的单元化实例上调用print。发生这种情况正是因为超构造函数在子类构造函数之前运行。

总之,从构造函数调用重写的方法会导致不必要的执行顺序反转:控制权从超类构造函数转移到子类方法中。

至于你关于为什么打印5的问题:a.print()a.name不同,是一种受动态调度约束的方法调用。所以不管a声明的类型是什么(Alpha(,Beta#print方法都会被调用(同样的情况发生在Alpha的构造函数中,但在i变量初始化之前(。

class Alpha
{
       String name = "Alpha";
       Alpha()
       {
           print();

这里的这个实际上调用了Beta.print(),因为它@Override Alpha.print()。由于首先调用基类构造函数,因此Beta部分尚未在此处初始化,因此它打印 0,因为...

       }
       void print()
       {
           System.out.println("Alpha Constructor");
       }
}
class Beta extends Alpha
{
       int i =   5;

这行代码尚未执行。类体内的初始化在超类构造函数(super()(之后执行,但在同一类的构造函数主体之前执行。

       String name = "Beta";
       public static void main(String[] args)
       {
          Alpha a = new Beta();
          a.print();//Line1 executes Beta constructor

在这里,它将打印 5,因为 Beta (a( 的初始化完成。

          System.out.println(a.name);//Line 2 displays Alpha instance variable
       }

而这句话实际上叫哔的方法:

       void print()
       {
           System.out.println(i);
       }
}

初始化/调用顺序:

  • Object.Object()
  • Alpha.<instance vars>
  • Alpha.<constructor body>
    • 覆盖Alpha.print()Beta.print()(打印Beta.i,仍为 0(默认值(
  • Beta.<instance vars>(此处Beta.i将初始化为 5(
  • Beta.<constructor body>
  • Beta.print()覆盖Alpha.print()(打印Beta.i,最终为 5,因为初始化已完成(

我不明白为什么 Alpha 的构造函数没有首先被执行。

它确实首先执行。我不知道是什么让你认为它没有。也许输出0.这是因为您在构造函数中调用了重写的方法,该方法将在类中调用print() Beta方法。现在,由于此时变量i尚未初始化,因此它将打印 0。有关更多详细信息,请参阅此答案。我还写了一篇关于这个主题的博客文章。

我相信"super(("首先会被每个子构造函数隐式调用......右?

并非总是如此。当您使用 this() 链接同一类的构造函数时,不会添加super()。否则超类构造函数将被链接。

如果 Beta 的构造函数已经执行,那么为什么要打印"5"?

为什么不呢?Beta 构造函数将使用 5 初始化i。在声明点执行的初始化由编译器移动到类的每个构造函数,在 super()this() 语句之后,无论存在什么。

  • Alpha 的构造函数确实首先被执行。如果您将System.out.println("Alpha Ctr");放入Alpha()方法中,您会注意到Alpha Ctr被打印。
  • 事实是,您已经覆盖了子类Beta中的print()方法。因此,Beta#print()被执行而不是Alpha#print()

如果你稍微改变Beta#print()这个行为会更清晰:

void print() {
    System.out.println(name + ": " + i);
}

现在将打印:

null: 0
Beta: 5
Alpha

这里打印null: 0因为变量name & i在构造父类Alpha时未初始化。

最新更新