具有继承调用的构造函数中的java最终变量
我刚刚发现了一些很奇怪的事情。如果使用重写方法从隐式超级构造函数调用最终变量,则在调用时永远不会初始化元素:
public static abstract class A {
public A()
{
doSomething();
}
public abstract void doSomething();
}
public static class B extends A {
private final Object s = new Object();
public B()
{
}
public void doSomething() {
System.out.println(s);
}
}
public static void main( String[] args )
{
new B();// prints 'null'
}
如果未重写该方法,则将正确实例化最终变量:
public static class B {
private final Object s = new Object();
public B()
{
doSomething();
}
public void doSomething() {
System.out.println(s);
}
}
public static void main( String[] args )
{
new B(); // prints the object correctly
}
最后,对我来说更奇怪的是(我认为这与字符串#intern机制有关)
public static abstract class A {
public A()
{
doSomething();
}
public abstract void doSomething();
}
public static class B extends A {
private final String s = "Hello";
public B()
{
}
public void doSomething() {
System.out.println(s);
}
}
public static void main( String[] args )
{
new B(); // will print "Hello"
}
我的问题是,在第一种情况下,我能做些什么来解决这个问题,我应该使用确保非空值的getter吗
我有点理解为什么会出现第一种情况(构造函数在初始化任何实例变量之前隐式调用“super”构造函数),但是,如果我是正确的,在这种情况下,为什么第三种情况会正确打印“Hello”
# 1 楼答案
理解基类的构造函数是在子类的构造函数之前执行的,这一点很重要。这意味着,在基类的构造过程中,子类的字段可能尚未初始化。(但是,它们将在子类的构造过程中初始化。)
您发现的问题是永远不要从构造函数中调用可重写方法的原因之一
getter可能同样糟糕,因为getter也是可重写的
而不是
在
B
中,可以将要在A
的构造中使用的变量作为参数传递给A
的构造函数:这将传递与构造
B
对象相关的数据,以便B
的构造函数是“自包含的”。这是相当混乱的,我建议你重新考虑你的课程结构关于第三个案件:
由于
"Hello"
是一个编译时常量表达式,并且s
是最终的,因此Java编译器可以自由地内联使用s
,也就是说,可以自行决定将s
替换为"Hello"