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.导致Beta#print
从Alpha
的构造函数中调用,这会导致打印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
时未初始化。