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

Tom*_*ito 42 java null static 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) {}
}
Run Code Online (Sandbox Code Playgroud)

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

更新

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

Pyr*_*cal 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);
  }
}
Run Code Online (Sandbox Code Playgroud)

这将打印:

null
null
myClassObject
null
Run Code Online (Sandbox Code Playgroud)

编辑

好吧,让我们更清楚地说明这一点.

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

明白了吗?

编辑2

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

  • ..因为`myClass`**本身**是静态的. (3认同)
  • @Pyrolistical:当我执行你的程序时,我得到了不同的结果.它打印了`null null myClassObjectref null` (2认同)

Kev*_*ock 25

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

这是JVM首次引用类时所经历的顺序MyClass.

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

请记住,Java指定为final变量赋值一次.并不是保证在代码引用它时为其分配值,除非您确保代码在分配后引用它.

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

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


Sla*_*hev 19

那是因为Java按照声明的顺序执行静态部分.在你的情况下,顺序是

  1. 新的MyClass
  2. 新对象

当执行#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
  }
}
Run Code Online (Sandbox Code Playgroud)

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

class MyClass {

  private static final MyClass myClass = new MyClass();

  private Object obj = new Object();

  private MyClass() {
    System.out.println(obj); // will print null once
  }
}
Run Code Online (Sandbox Code Playgroud)

  • 静态成员*是在实例构造函数运行之前构造的.只是在你的代码中,你的静态初始化器*也会调用你的构造函数.这是一个鸡蛋问题. (3认同)