鉴于Main.java:
public class Main{
public static void main(String[]args){
A a = new B();
a.print();
}
}
class A{
A() {print();}
void print() { System.out.println("A"); }
}
class B extends A{
int i = 4;
void print() { System.out.println(i); }
}
结果:
0
但是,如果a.print引用A类,为什么不输出“A”?在这种情况下,我如何知道一种方法何时会被调用?为什么A的构造函数被调用并且仍在使用B的方法?
答案 0 :(得分:5)
由于polymorphism,致电a.print()
打印4
。调用的方法取决于a
的运行时类型,即B
。什么时候召唤它并不重要;多态性始终适用。
两次都会调用B
的{{1}}方法。一次来自print
的构造函数,由A
中的默认构造函数调用。另一次是您在B
中的明确调用。
第一次打印产生main
而不是0
的原因是因为在调用4
时,print
仍在构建中。也就是说,A
构造函数仍在执行中。在超类构造函数返回之前,尚未在子类中初始化任何内容,甚至不是变量初始值设定项。值A
在超类构造函数完成之后但在子类构造函数的其余部分完成之前分配。由于变量初始值设定项尚未运行,因此4
的默认值(0
为false
,对象为boolean
}的值为{{ 1}}在第一次印刷中。
此订单由JLS, Section 12.5列出:
在作为结果返回对新创建的对象的引用之前,处理指示的构造函数以使用以下过程初始化新对象:
将构造函数的参数分配给此构造函数调用的新创建的参数变量。
如果此构造函数以同一个类中的另一个构造函数的显式构造函数调用(第8.8.7.1节)开头(使用此方法),则使用这五个相同步骤计算参数并以递归方式处理该构造函数调用。如果该构造函数调用突然完成,则此过程突然完成,原因相同;否则,继续步骤5.
此构造函数不以同一类中另一个构造函数的显式构造函数调用开头(使用此方法)。如果此构造函数用于Object以外的类,则此构造函数将以超类构造函数的显式或隐式调用开始(使用super)。使用这五个相同的步骤评估参数并递归处理超类构造函数调用。如果该构造函数调用突然完成,则此过程突然完成,原因相同。否则,请继续执行步骤4.
为此类执行实例初始值设定项和实例变量初始值设定项,将实例变量初始值设定项的值分配给相应的实例变量,按从而出现的从左到右的顺序在文本的源代码中。如果执行任何这些初始值设定项导致异常,则不会处理其他初始化程序,并且此过程会突然完成同样的异常。否则,请继续步骤5.
- 醇>
执行此构造函数的其余部分。如果执行突然完成,则此过程突然完成,原因相同。否则,此过程正常完成。
(大胆强调我的)
这是为什么调用可以从构造函数重写的方法的一个不好主意的示例。子类状态尚未初始化。
答案 1 :(得分:1)
如果是重写方法(例如示例中的print()), object 类型决定调用哪个方法,而不是 reference 类型。
答案 2 :(得分:0)
B没有任何构造函数,因此除了调用A的构造函数之外,它的默认构造函数不会做任何事情。
现在,当调用B的默认构造函数时,它会调用A的构造函数(请记住i
仍未设置,因此默认值为0)。一个构造函数调用print(),现在对象实际上是B,它调用B&#39的print()并且它打印0(记住i
没有设置)。
现在,一旦这些构造函数调用完成,B代码就会执行,将i设置为0.现在调用print()将再次进入B>的print()(因为对象只有B)将打印4。
"调试是关键"