为什么静态字段没有及时初始化?

时间:2010-03-30 18:39:08

标签: java static null nullpointerexception

以下代码打印null一次。

class MyClass {
   private static MyClass myClass = new MyClass();
   private static final Object obj = new Object();
   public MyClass() {
      System.out.println(obj);
   }
   public static void main(String[] args) {}
}

为什么在构造函数运行之前没有初始化静态对象?

更新

我只是在没有注意的情况下复制了这个示例程序,我以为我们正在讨论2个Object字段,现在我看到第一个是MyClass字段..:/

5 个答案:

答案 0 :(得分:37)

因为静态按照源代码中给出的顺序进行初始化。

检查出来:

class MyClass {
  private static MyClass myClass = new MyClass();
  private static MyClass myClass2 = new MyClass();
  public MyClass() {
    System.out.println(myClass);
    System.out.println(myClass2);
  }
}

那将打印:

null
null
myClassObject
null

修改

好的,让我们更清楚地说明这一点。

  1. 按照源代码中声明的顺序逐个初始化静态。
  2. 由于第一个静态在其余静态之前初始化,因此在初始化期间,其余静态字段为空或默认值。
  3. 在第二个静态启动期间,第一个静态是正确的,但其余静态仍为空或默认。
  4. 这是清楚的吗?

    编辑2

    正如Varman所指出的,在初始化时,对它自身的引用将为null。如果你考虑一下,这是有道理的。

答案 1 :(得分:22)

让我们尝试一种不同的方式来解释这个......

这是JVM首次引用类MyClass时所经历的序列。

  1. 将字节码加载到内存中。
  2. 清除静态存储的内存(二进制零)。
  3. 初始化课程:
    1. 按照显示的顺序执行每个静态初始化程序,包括静态变量和static { ... }块。
    2. JVM然后将您的myClass静态变量初始化为MyClass的新实例。
    3. 当发生这种情况时,JVM会注意到MyClass已经加载(字节码)并且正在初始化,因此它会跳过初始化。
    4. 在堆上为对象分配内存。
    5. 执行构造函数。
    6. 打印出仍为obj的{​​{1}}的值(因为它不是堆和构造函数初始化变量的一部分)。
    7. 构造函数完成后,执行下一个静态初始值设定项,将null设置为obj的新实例。
  4. 完成类初始化。从这一点开始,所有构造函数调用都将按照您的假设/期望行事 - Object不是obj,而是对null实例的引用。
  5. 请记住,Java指定为Object变量赋值一次。并不是保证在代码引用它时为其分配值,除非您确保代码在分配后引用它。

    这不是错误。这是在自己的初始化期间处理类使用的定义方法。如果不是这样,那么JVM将进入无限循环。请参阅步骤#3.3(如果JVM没有跳过正在初始化过程中的类的初始化,它将继续初始化它 - 无限循环)。

    请注意,这一切都发生在首次引用该类的同一个线程上。其次,JVM保证在允许任何其他线程使用此类之前完成初始化。

答案 2 :(得分:19)

这是因为Java按照声明的顺序执行静态部分。在您的情况下,序列是

  1. new MyClass
  2. new Object
  3. 当执行#1时,obj仍未初始化,因此它打印为null。尝试以下操作,您将看到不同之处:

    class MyClass {
      private static final Object obj = new Object();
      private static MyClass myClass = new MyClass();
      public MyClass() {
        System.out.println(obj); // will print null once
      }
    }
    

    一般来说,最好一起避免这种结构。如果您正在尝试创建单例,那么代码片段应该是这样的:

    class MyClass {
    
      private static final MyClass myClass = new MyClass();
    
      private Object obj = new Object();
    
      private MyClass() {
        System.out.println(obj); // will print null once
      }
    }
    

答案 3 :(得分:0)

这是因为静态字段按照它们定义的相同顺序初始化。

答案 4 :(得分:0)

@Pyrolistical

因为第一个静态字段的初始化myclass没有完全构造...我得到的结果是

空 空值 testInitialize.MyObject@70f9f9d8 空