为什么成员对象在超类的构造函数之后初始化?

问题描述:

昨天我遇到了一个有趣的问题,虽然修复很简单,但我仍然对它的“原因”有些模糊。为什么成员对象在超类的构造函数之后初始化?

我有一个类有一个私有成员变量,当它被实例化时被分配,但是如果它被用在一个由超类的构造函数调用的抽象函数中,变量没有值。该问题的解决方案非常简单,我只需将变量声明为静态并且它的分配正确。一些代码来说明这个问题:

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特定的?

感谢您的任何见解!

+2

小心从超类ctor调用子类方法。 – 2012-01-12 23:27:03

+0

该代码实际上打印fooBar!因为'bar'变量是最终的,这使得它成为编译时常量。如果没有'final',它会打印出null。 – x22 2012-01-12 23:56:33

+0

@ x22这实际上是不正确的,是什么引起了这种情况的调查 - 当我最初编写它时,我相信同样的事情。 – 2012-01-13 00:05:50

bar = "fooBar!";的赋值是内嵌在编译期间进入构造函数。

超类的构造函数运行before子类的构造函数,因此只有在事后才执行语句。

一般来说,它是bad practice从构造函数调用可重写的方法。

+0

真棒,感谢您的链接,帮助一堆! – 2012-01-12 23:34:06

它由Java语言规范定义。将其改为静态在现实世界中几乎不会是可接受的解决方案。

JLS 4.12.5 Initial Values of VariablesJLS 8.3.2 Initialization of Fields

总体而言,这是不好的做法来调用构造函数的非最终方法。原因在于它可以(并且如果该方法是抽象的,那么肯定会)在尚未初始化的类中调用方法:当执行new Foo()时,构造函数在构造函数之前被调用,因此Foo构造函数Foo.initialize实质上是在一个尚未完全构造的对象上工作。