在声明初始化的数组直到调用超级构造函数之后才实际初始化

时间:2014-05-20 22:42:46

标签: java oop

我遇到了一个看似奇怪的问题,虽然我知道这个设计背后可能有一些原因,并且想知道为什么会这样。

考虑两个类,Foo和Bar。 Bar扩展了Foo并覆盖了Foo实现的方法。 Foo非常简单:

public class Foo {
    public Foo() {
        System.out.println("Foo constructor");
        someMethod();
    }

    public void someMethod() {
        System.out.println("Foo.someMethod");
    }
}

Bar就是问题所在。它定义了一个在构造函数外部初始化的数组(我不知道这个术语是什么)。我注意到当从Foo调用someMethod时,数组尚未初始化,但是在调用super()之后,数组IS被初始化。这是Bar:

public class Bar extends Foo {
    int[] a = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };

    public Bar() {
        super();
        System.out.println("FooBar constructor");
        if (a == null) System.out.println("FooBar a is null");
    }

    public void someMethod() {
        if (a == null) System.out.println("FooBar.someMethod a is null");
    }
}

这没有多大意义。我想从超级构造函数中调用的方法访问该数组,但该数组未初始化。我试着想出一个解决方案。我的第一个想法是在Bar的构造函数中初始化数组,但在调用super之前仍然不会初始化它。

作为参考,输出如下:

Foo constructor
FooBar.someMethod a is null
FooBar constructor

为什么Java是这样设计的,最好的方法是什么?

2 个答案:

答案 0 :(得分:4)

超类构造函数必须在子类构造函数的其余部分开始之前成功完成。只有这样才能初始化子类变量,因此在超类构造函数中,a中的Bar仍为null。出现这种奇怪的情况是因为超类构造函数调用的方法在子类中被重写,因此在子类本身初始化之前调用子类方法。

有关其原因Java leaking this in constructor的详细信息。这使得未完全初始化的对象可以被其他对象访问。

这里最好的做法是不要让超类构造函数调用在子类中被覆盖的方法。如果愿意,让Bar自己初始化/操纵a

答案 1 :(得分:1)

超类的构造函数必须在子类构造函数中完成任何操作之前完成。这将包括任何初始化。