为什么成员对象在超类的构造函数之后初始化?
昨天我遇到了一个有趣的问题,虽然修复很简单,但我仍然对它的“原因”有些模糊。为什么成员对象在超类的构造函数之后初始化?
我有一个类有一个私有成员变量,当它被实例化时被分配,但是如果它被用在一个由超类的构造函数调用的抽象函数中,变量没有值。该问题的解决方案非常简单,我只需将变量声明为静态并且它的分配正确。一些代码来说明这个问题:
class Foo extends BaseClass
{
private final String bar = "fooBar!";
public Foo()
{
super();
}
@Override
public void initialize()
{
System.out.println(bar);
}
}
和基础类:
abstract class BaseClass
{
public BaseClass()
{
initialize();
}
public abstract void initialize();
}
在这个例子中,当我们调用new Foo();
它将输出(空)而不是预期Foobar的!
由于我们实例化了一个Foo类型的对象,它的成员是否应该在调用其成员(因此它的超类)的构造函数之前不被分配和赋值?这是在Java语言的某处指定的,还是JVM特定的?
感谢您的任何见解!
bar = "fooBar!";
的赋值是内嵌在编译期间进入构造函数。
超类的构造函数运行before子类的构造函数,因此只有在事后才执行语句。
一般来说,它是bad practice从构造函数调用可重写的方法。
真棒,感谢您的链接,帮助一堆! – 2012-01-12 23:34:06
它由Java语言规范定义。将其改为静态在现实世界中几乎不会是可接受的解决方案。
见JLS 4.12.5 Initial Values of Variables和JLS 8.3.2 Initialization of Fields
总体而言,这是不好的做法来调用构造函数的非最终方法。原因在于它可以(并且如果该方法是抽象的,那么肯定会)在尚未初始化的类中调用方法:当执行new Foo()
时,构造函数在构造函数之前被调用,因此Foo
构造函数Foo.initialize
实质上是在一个尚未完全构造的对象上工作。
小心从超类ctor调用子类方法。 – 2012-01-12 23:27:03
该代码实际上打印fooBar!因为'bar'变量是最终的,这使得它成为编译时常量。如果没有'final',它会打印出null。 – x22 2012-01-12 23:56:33
@ x22这实际上是不正确的,是什么引起了这种情况的调查 - 当我最初编写它时,我相信同样的事情。 – 2012-01-13 00:05:50