为什么Object的值为null,即使它已被初始化?

时间:2015-06-16 22:20:10

标签: java

我遇到以下程序的问题,它有两个类A和B,一个方法getObject()在A类中已经在B类中重写,B类扩展了A类。

我从基类构造函数中调用getObject(),我相信它会在类B中调用重写方法,并且在运行时错误是空指针异常,为什么Object尚未初始化,即使它有?

class A {
    Object object = new Object();

    public A() {
        System.out.println("A Class");
        getObject();
    }

    public void getObject() {
        System.out.println("Class A Version");
        System.out.println(object.toString());
    }
}

class B extends A {

    Object object = new Object();

    public B() {
        System.out.println("B Class");
    }

    @Override
    public void getObject() {
        System.out.println("Class B Version");
        System.out.println(object.toString());
    }
}

public class Init {
    public static void main(String[] args) {
        new B();
    }
}

输出

A Class
Exception in thread "main" Class B Version
java.lang.NullPointerException
    at net.mindview.util.B.getObject(Init.java:28)
    at net.mindview.util.A.<init>(Init.java:8)
    at net.mindview.util.B.<init>(Init.java:21)
    at net.mindview.util.Init.main(Init.java:34)

6 个答案:

答案 0 :(得分:4)

根据this answer

  

正确的初始化顺序是:

     
      
  1. 静态变量初始化和静态初始化块,按文本顺序排列,如果该类之前尚未初始化。
  2.   
  3. 构造函数中的super()调用,无论是显式还是隐式。
  4.   
  5. 实例变量初始化程序和实例初始化程序段,按文本顺序排列。
  6.   
  7. super()之后的剩余构造函数体。
  8.         

    请参阅Java虚拟机规范的第2.17.5-6节。

基本上,类object中的B在正在执行类A的构造函数期间尚未初始化(第2点:隐式super()调用)。

答案 1 :(得分:3)

在B构造函数中,您调用一个使用B&#39的getObject()的构造,该构造使用的不是来自B的初始化对象。 逐步使用调试器,您将看到流程。

答案 2 :(得分:2)

NPE来自班级本身,而不是object。构造函数工作时,this为空。

B扩展了A,因此插入了隐式super构造函数调用。这会打印你的&#34; A Class&#34;但是,方法调用等同于this.getObject();this为空。

答案 3 :(得分:1)

我认为这个问题的关键点在于你应该知道超类的构造函数(以防万一)将首先被调用。让我们假设A也扩展C的另一个条件,以防c的构造函数首先被调用。

所以调用的顺序应该是:1个C的构造函数,2个构造函数A,最后是B的构造函数。

对于您的情况,因为首先调用了构造函数A,此时对象尚未初始化,导致空指针除外

答案 4 :(得分:1)

执行命令是:

class A {

    Object object = new Object(); // --------------- 2

    public A() {
        System.out.println("A Class"); // ---------- 3
        getObject(); // ---------------------------- 4 (calls B.getObject())
    }

    public void getObject() {
        System.out.println("Class A Version");
        System.out.println(object.toString());
    }
}

class B extends A {

    Object object = new Object(); // --------------- 7 (not executed)

    public B() {
        System.out.println("B Class"); // ---------- 8 (not executed)
    }

    @Override
    public void getObject() {
        System.out.println("Class B Version"); // -- 5
        System.out.println(object.toString()); // -- 6 (exception!)
    }
}

public class Init {
    public static void main(String[] args) {
        new B();  // ------------------------------- 1
    }
}

答案 5 :(得分:1)

你永远不应该在构造函数中调用overridable方法,因为它引入了调用方法的歧义。在你的情况下,你的超级A调用B的getObject()。此时B点尚未实例化,因此呼叫失败并为您提供NPE。