超级调用时变量为空

时间:2014-06-20 12:57:09

标签: java nullpointerexception null superclass super

我正在使用Java 7并获得3个类:

TestSuper.java

public abstract class TestSuper {
    public TestSuper() {
            testMethod();
    }

    protected abstract void testMethod();
}

TestNull.java

public class TestNull extends TestSuper {
    private String test = "Test";

    public TestNull() {
            super();
            System.out.println(test);
    }

    @Override
    protected void testMethod() {
            System.out.println(test);
    }

}

TestMain.java

public class TestMain {
    public static void main(String[] args) {
            new TestNull();
    }
}

输出:

null
Test

为什么会发生这种情况并且有一个很好的解决方法呢?

4 个答案:

答案 0 :(得分:7)

当您致电new TestNull();时,您正在调用类TestNull的构造函数,它调用super()构造函数:它包含对TestNull中实现的方法的调用在那里打印字符串字段,此时子类TestNull的字段尚未初始化,即为空。

在超级构造函数调用之后,将初始化所有字段,因此第二个打印实际上显示(初始化)字符串的新值。

这里的关键点是,在超类的实例化之后,初始化了一个子类的字段。

解决方法?这取决于你想要的确切行为:也许在超级构造函数中调用抽象方法是有意义的(即在TestSuper类的构造函数中)。

答案 1 :(得分:2)

根据JLS 8.1.1.1 Abstract Class

  

抽象类的子类本身不是抽象的   实例化,导致执行构造函数   抽象类和,因此执行 字段初始值设定项   例如该类的变量。

答案 2 :(得分:1)

您正在构造函数中调用可覆盖的实例方法(在您的情况下也调用实例字段private String test = "Test";)。这可能会导致不一致,因为实例未完全构造。这是一种不好的做法,所以要避免它:

public TestSuper() {
       testMethod();
}

请阅读此主题:What's wrong with overridable method calls in constructors?

答案 3 :(得分:0)

您可以通过将testMethod()的调用移到单独的函数来解决此问题

public abstract class TestSuper {
    public TestSuper() { }

    public void callTestMethod(){
      testMethod();
    }

    protected abstract void testMethod();
}

然后在callTestMethod()构造函数上调用TestNull

public TestNull() {
      super.callTestMethod();
      System.out.println(test);
}