Java 构造函数中具有继承调用的最后一个变量
我刚刚发现了一些很奇怪的事情。如果使用重写方法从隐式超级构造函数调用最终变量,则在调用时永远不会初始化元素:Java 构造函数中具有继承调用的最后一个变量,java,Java,我刚刚发现了一些很奇怪的事情。如果使用重写方法从隐式超级构造函数调用最终变量,则在调用时永远不会初始化元素: public static abstract class A { public A() { doSomething(); } public abstract void doSomething(); } public static class B extends A { private final Object s = n
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机制有关)
我的问题是,在第一种情况下,我能做些什么来解决这个问题,我应该使用确保非空值的getter吗
我有点理解为什么会出现第一种情况(构造函数在初始化任何实例变量之前隐式调用“super”构造函数),但是,如果我是正确的,在这种情况下,为什么第3个案例正确地打印“Hello”?重要的是要理解基类的构造函数是在子类的构造函数之前执行的。这意味着,在基类的构造过程中,子类的字段可能尚未初始化。(但是,它们将在子类的构造过程中初始化。) 我的问题是,在第一种情况下,我能做些什么来解决这个问题,我应该使用确保非空值的getter吗 您发现的问题是永远不要从构造函数中调用可重写方法的原因之一 getter可能同样糟糕,因为getter也是可重写的 而不是
Object s = new Object();
...
public void doSomething() {
System.out.println(s);
}
在B
中,您可以将用于A
构造的变量作为参数传递给A
s构造函数:
public B() {
super(new Object());
}
这将传递与构造B
对象相关的数据,以便B
的构造函数是“自包含的”。这是相当混乱的,我建议你重新考虑你的课程结构
关于第三个案件:
private final String s = "Hello";
由于
“Hello”
是一个编译时常量表达式,而且s
是最终表达式,因此Java编译器可以自由地内联使用s
,也就是说,可以随意将s
替换为“Hello”
。我认为第三种情况基本上是一种优化,因为它是用常量值初始化的。尝试使用“Hello”.toString()
,我想您会看到不同的结果。“修复”不是从构造函数中调用非final方法……我希望看到一个具体的例子(真实的类-不是a
和B
),其中这是一个实际问题。我是在一个真实的案例之后创建这些类的application@JonSkeet-什么时候有人会使用“你好”.toString()?@TheLostMind:当他们试图演示常量值和非常量值之间的差异时:),但这太混乱了。也许值得在最后一行详细说明。我刚刚检查过,看起来这个字段在调用时并没有初始化——但是Java编译器正在方法调用中内联s
的值。我想这就是你的意思,但更清楚一点是好的:)@Qix,我同意。但我想说,这是让子类影响基类构造的想法所固有的。@JonSkeet出于好奇,C#也可以“内联”使用编译时常量表达式。(请注意,我从未写过一行C#)如果您对您可以参考的建议解决方案不满意。如果这些答案也不令人满意(我认为这些答案中没有一个是正确的),你可以悬赏。我相信这会产生一些好的答案。
private final String s = "Hello";