我刚刚发现了一些非常奇怪的东西。如果使用overriden方法从隐式超级构造函数调用最终变量,则在调用时永远不会初始化该元素:
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'构造函数),但是,如果我是正确的,在这种情况下为什么第3种情况正确打印'Hello' ?
答案 0 :(得分:2)
理解基类的构造函数在子类的构造函数之前执行是很重要的。这意味着在构造基类期间可能尚未初始化子类的字段。 (然而,将 在构建子类时初始化。)
我的问题是,如果我使用确保非空值的getter,我可以在第一种情况下做些什么来解决这个问题?
您发现的问题是从未在构造函数中调用可覆盖方法的原因之一。
吸气剂可能同样糟糕,因为吸气剂也会被覆盖。
而不是
Object s = new Object();
...
public void doSomething() {
System.out.println(s);
}
<{1>}中的,您可以将B
构造中使用的变量作为A
构造函数的参数传递:
A
这传递了与构造public B() {
super(new Object());
}
对象相关的数据,因此B
的构造函数是“自包含的”。这很麻烦,我建议你重新考虑你的课程结构。
关于第三种情况:
B
由于private final String s = "Hello";
是编译时常量表达式,并且"Hello"
是最终的,因此Java编译器可以自由地使用s
,即将s
替换为s
可自行决定。