我刚刚发现了一些很奇怪的事情。如果使用重写方法从隐式 super 构造函数调用最终变量,则该元素将永远不会在调用时初始化:
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
}
最后,对我来说更奇怪(我认为这与 String#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” ?
最佳答案
重要的是要理解基类的构造函数在子类的构造函数之前执行。这意味着子类的字段可能在基类构造期间尚未初始化。 (然而,它们将在子类的构造过程中被初始化。)
My question is what can i do in the first case to fix this, should i use a getter that ensures non-null value ?
您发现的问题是永远不要从构造函数中调用可重写方法的原因之一。
getter 可能同样糟糕,因为 getter 也可以被重写。
而不是
Object s = new Object();
...
public void doSomething() {
System.out.println(s);
}
在B
中,您可以将构造A
时使用的变量作为参数传递给A
的构造函数:
public B() {
super(new Object());
}
这会传递与构造 B
对象相关的数据,以便 B
的构造函数是“自包含的”。但这相当困惑,我建议您重新考虑类的结构。
关于第三种情况:
private final String s = "Hello";
由于 "Hello"
是编译时常量表达式,并且 s
是最终的,因此 Java 编译器可以自由内联使用 s
,即自行决定将 s
替换为 "Hello"
。
关于java - 具有继承调用的构造函数中的最终变量,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/29347490/