Java 构造函数-执行顺序

Java 构造函数-执行顺序,java,Java,此程序成功编译并显示以下输出 class Alpha { String name = "Alpha"; Alpha() { print(); } void print() { System.out.println("Alpha Constructor"); } } class Beta extends Alpha { int i = 5;

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

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);
       }
}
问题

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

我相信每个子构造函数首先都会隐式调用super…对吗

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


我有点理解的第三行,即Alpha自己的变量将显示,因为尚未对实例变量执行强制转换

您在这里犯了两项重罪:

从构造函数调用可重写的方法; 在子类中声明与超类中的实例变量同名的实例变量。 这两种习语都会导致令人惊讶的行为。具体来说,1。结果从Alpha的构造函数调用Betaprint,这将导致打印0,因为您在一个统一化的Beta实例上调用print。这正是因为超级构造函数在子类构造函数之前运行

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


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

您在这里犯下了两项重罪:

从构造函数调用可重写的方法; 在子类中声明与超类中的实例变量同名的实例变量。 这两种习语都会导致令人惊讶的行为。具体来说,1。结果从Alpha的构造函数调用Betaprint,这将导致打印0,因为您在一个统一化的Beta实例上调用print。这正是因为超级构造函数在子类构造函数之前运行

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

关于为什么打印5的问题:与a.name不同,a.print是一个受动态调度约束的方法调用。因此,不管a的声明类型是Alpha,都会调用Betaprint方法,就像在Alpha的构造函数中一样,但在初始化i变量之前

Alpha的构造函数确实首先执行。如果您将System.out.printlnAlpha Ctr;在Alpha方法中,您会注意到Alpha Ctr正在打印。 事实上,您已经在子类Beta中重写了print方法。因此,将执行Betaprint而不是Alphaprint。 如果您稍微更改Betaprint,此行为将更加清楚:

0
5
Alpha
现在将打印:

void print() {
    System.out.println(name + ": " + i);
}
此处为null:0,因为在构建父类Alpha时变量name&i未初始化

Alpha的构造函数确实首先执行。如果您将System.out.printlnAlpha Ctr;在Alpha方法中,您会注意到Alpha Ctr正在打印。 事实上,您已经在子类Beta中重写了print方法。因此,将执行Betaprint而不是Alphaprint。 如果您稍微更改Betaprint,此行为将更加清楚:

0
5
Alpha
现在将打印:

void print() {
    System.out.println(name + ": " + i);
}
此处为null:0,因为在构建父类Alpha时变量name&i未初始化

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

它确实是先执行的。我不知道是什么让你觉得没有。也许输出为0。这是因为您在构造函数中调用了被重写的方法,该方法将在Beta类中调用print方法。现在,因为在那个时候,变量i还没有初始化,它将打印0。有关更多详细信息,请参阅。我也写了一篇关于这个话题的文章

我相信每个子构造函数都会首先隐式调用super…对吗

不总是这样。当您使用此链接同一类的构造函数时,将不会添加super。否则将链接超级类构造函数

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

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

null: 0
Beta: 5
Alpha
我不明白为什么Alpha的构造函数没有首先执行

它确实是先执行的。我不知道是什么让你觉得没有。也许输出为0。这是因为您在构造函数中调用了被重写的方法,该方法将在Beta类中调用print方法。现在,因为在那个时候,变量i还没有初始化,它将打印0。有关更多详细信息,请参阅。我还写了一篇文章 关于这个话题

我相信每个子构造函数都会首先隐式调用super…对吗

不总是这样。当您使用此链接同一类的构造函数时,将不会添加super。否则将链接超级类构造函数

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

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

null: 0
Beta: 5
Alpha
这里的这个实际上调用了Beta.print,因为它@覆盖了Alpha.print。由于基类构造函数是首先调用的,测试部分还没有在这里初始化,因此它打印0,因为

class Alpha
{
       String name = "Alpha";
       Alpha()
       {
           print();
这行代码尚未执行。类主体内的初始化在超级类构造函数super之后但在同一类的构造函数主体之前执行

       }
       void print()
       {
           System.out.println("Alpha Constructor");
       }
}
class Beta extends Alpha
{
       int i =   5;
在这里,随着Beta的初始化完成,它将打印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
       }
初始化/调用顺序:

对象,对象 阿尔法 阿尔法 覆盖Alpha.print的Beta.print打印Beta.i,默认值仍然为0 贝塔。这里是Beta。我将被初始化为5 贝塔。 覆盖Alpha.print的Beta.print将打印Beta.i,最终为5,因为初始化已完成 这里的这个实际上调用了Beta.print,因为它@覆盖了Alpha.print。由于基类构造函数是首先调用的,测试部分还没有在这里初始化,因此它打印0,因为

class Alpha
{
       String name = "Alpha";
       Alpha()
       {
           print();
这行代码尚未执行。类主体内的初始化在超级类构造函数super之后但在同一类的构造函数主体之前执行

       }
       void print()
       {
           System.out.println("Alpha Constructor");
       }
}
class Beta extends Alpha
{
       int i =   5;
在这里,随着Beta的初始化完成,它将打印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
       }
初始化/调用顺序:

对象,对象 阿尔法 阿尔法 覆盖Alpha.print的Beta.print打印Beta.i,默认值仍然为0 贝塔。这里是Beta。我将被初始化为5 贝塔。 覆盖Alpha.print的Beta.print将打印Beta.i,最终为5,因为初始化已完成
0从何而来?0是第一次打印的输出,其中i尚未被覆盖。两个构造函数都调用print,但在Alphaconstructor调用print``时,实例初始值设定项尚未初始化i。0来自何处?0是第一次运行print的输出,其中i尚未被重写。两个构造函数都调用print,但在Alphaconstructor调用print时,``我还没有被实例初始值设定项初始化。非常好的解释感谢创建解释感谢第一次看到你的博客…你应该在你的博客中写一些关于regex的东西…@LolCoder。当然如果时间允许,更重要的是,当我觉得我有什么有趣的东西可以写的时候,我肯定会把它贴出来。谢谢:很高兴第一次看到你的博客…你应该在你的博客中写一些关于正则表达式的东西…@LolCoder。当然如果时间允许,更重要的是,当我觉得我有什么有趣的东西可以写的时候,我肯定会把它贴出来。谢谢: