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”实例变量进行转换)
答案 0 :(得分:5)
你在这里犯了两个“重罪”:
两种习语都会导致令人惊讶的行为。具体来说,1. Beta#print
从Alpha
的构造函数调用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)
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
未初始化。