考虑以下课程:
public class A {
String bar = "A.bar";
A() { foo(); }
public void foo() {
System.out.println("A.foo(): bar = " +
bar);
}
}
public class B extends A {
String bar = "B.bar";
B() { foo(); }
public void foo() {
System.out.println("B.foo(): bar = " +
bar);
}
}
public class C {
public static void main(String[]
args) {
A a = new B();
System.out.println("a.bar = "
+ a.bar);
a.foo();
}
}
现在,打印的第一行是:B.foo(): bar = null
。根据我的理解,成员初始化在构造函数之前,但我可以看到这不是这种情况。
为什么此阶段B#bar
未初始化?
B#bar
何时初始化?
谢谢!
更新
在阅读完答案后,请看一下这个反例:
public class Bazz {
public int a = 42;
public Bazz() {
System.out.println(a);
}
public static void main(String[] args) {
Bazz bazz = new Bazz();
}
}
在这种情况下,a
不是0
。相反,Bazz()
的构造函数会打印42
有什么不同?
答案 0 :(得分:3)
让我列举一下步骤:
A a = new B();
//看到B是孩子,所以调用A的构造函数(B.bar尚未初始化)foo
中被调用。因为它被B覆盖所以B的foo
被调用并显示为空。B.bar
被初始化。注意:即使未在B中调用super()
,编译器也会自动添加此语句。
编辑:编译器如何看待B的构造函数:
超级();
this.bar =“B.bar”;
FOO();
更新:您的新结果来自我之前提供的相同解释:
Bazz bazz = new Bazz();
//当调用构造函数时,变量a初始化为42
System.out.println(a);
//打印42
实例变量在执行构造函数之前初始化,但是在执行super的构造函数之后。请参阅上面的编辑。
答案 1 :(得分:1)
您正在构建new B()
,而在构造函数B()
中,它会调用super()
。
因此,您的代码会调用A a = new B();
,该代码会落到System.out.println("B.foo(): bar = " + bar);
。此时a
尚未完成构建(未完全初始化),因此B.bar
为null
。当构造函数在bar
处执行步骤返回时,= "B.bar"
将使用}
进行初始化。
为什么此阶段
B#bar
未初始化?
因为a
未在System.out.println("B.foo(): bar = " + bar);
完全初始化。
B#bar
何时初始化?
}
(构造函数结束)。
记录中,这是STDOUT日志:
B.foo():bar = null //未初始化bar。我们在:
A() { foo(); }
B.foo():bar = B.bar // bar已初始化。我们在:
B() { foo(); }
a.bar = A.bar //你打电话给“println a.bar =
a.bar
”。在A a
中,bar
等于a.bar。B.foo():bar = B.bar // foo()是多态的,因为
a
是class B
,所以B.foo()
被调用,因此这一行。
@edit: Bazz.a
的类型为int
。 int
永远不会是null
,编译器会自动将这些变量替换为编译器传递的语句。因此System.out.println(a);
变为System.out.println(42);
。
答案 2 :(得分:0)
从类创建对象时,静态初始化程序首先运行。
它初始化静态属性和静态方法,
然后运行对象初始值设定项
首先,它运行对象B()
的构造函数,
所以非静态属性(bar
)稍后会被初始化。所以属性在执行构造函数后初始化。所以吧是null
如果您将bar声明为静态,
static String bar = "B.bar";
您可以先将条形变量初始化;
输出将是;
B.foo():bar = B.bar
B.foo():bar = B.bar
a.bar = A.bar
B.foo():bar = B.bar
答案 3 :(得分:0)
订单是:
A.bar
A
的构造函数B.bar
B
的构造函数请注意bar
中的A
与bar
中B
的变量不同。因为A
的构造函数调用了重写方法,所以此方法可以在B
初始化之前访问实例变量。