构造函数 - 执行顺序

时间:2013-08-25 10:53:40

标签: java

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自己的变量,因为尚未对“a”实例变量进行转换)

4 个答案:

答案 0 :(得分:5)

你在这里犯了两个“重罪”:

  1. 从构造函数中调用可覆盖的方法;
  2. 在子类中声明一个与超类中的实例变量同名的实例变量。
  3. 两种习语都会导致令人惊讶的行为。具体来说,1. Beta#printAlpha的构造函数调用0会导致打印print,因为您在{{1}的单元化实例上调用Beta }}。这恰好发生在,因为超级构造函数在子类构造函数之前运行。

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

    关于为什么打印5的问题:a.print()a.name不同,是一个受动态调度约束的方法调用。因此,无论a的声明类型(Alpha)如何,都会调用Beta#print方法(同样发生在Alpha的构造函数中,但在{i之前1}}变量已初始化。)

答案 1 :(得分:4)

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

这里实际调用Beta.print(),因为@Override s 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>
    • Beta.print()覆盖Alpha.print()(打印Beta.i,仍为0(默认值))
  • Beta.<instance vars>(此处Beta.i将初始化为5)
  • Beta.<constructor body>
  • Beta.print()覆盖Alpha.print()(打印Beta.i,最后是5,自初始化完成后)

答案 2 :(得分:1)

  

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

它首先执行了。我不知道是什么让你觉得它没有。也许是输出0。这是因为您在构造函数中调用了重写方法,该方法将调用print()类中的方法Beta。现在,由于在那个时间点,变量i尚未初始化,它将打印0.有关详细信息,请参阅this answer。我还在这个主题上写了blog post

  

我相信“super()”将首先由每个子构造函数隐式调用...对吗?

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

  

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

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

答案 3 :(得分:0)

  • Alpha的构造函数首先执行。如果您将System.out.println("Alpha Ctr");置于Alpha()方法中,则会发现Alpha Ctr已被打印。
  • 事实是,您已在子类print()中覆盖了Beta方法。因此Beta#print()执行而不是Alpha#print()

如果稍微改变Beta#print(),这种行为将更加明确:

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

现在将打印:

null: 0
Beta: 5
Alpha

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