53

以下代码打印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 字段.. :/

4

5 回答 5

45

因为静态是按照它们在源代码中给出的顺序初始化的。

看一下这个:

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. 在第二个静态启动期间,第一个静态是正确的,但其余的仍然是 null 或默认值。

明白了吗?

编辑 2

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

于 2010-03-30T18:43:23.047 回答
27

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

这是您第一次引用该类时 JVM 所经历的顺序MyClass

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

请记住,Java 规定一个final变量被赋值一次。除非您确保代码在分配后引用它,否则不能保证在代码引用它时为其分配一个值。

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

Note as well, this all happens on the same thread that first references the class. Second, the JVM guarantees that initialization will complete before any other thread is allowed to use this class.

于 2010-04-01T01:24:16.607 回答
19

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

  1. 新的我的班级
  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
  }
}

一般来说,最好一起避免这样的构造。如果您尝试创建一个单例,则该代码片段应如下所示:

class MyClass {

  private static final MyClass myClass = new MyClass();

  private Object obj = new Object();

  private MyClass() {
    System.out.println(obj); // will print null once
  }
}
于 2010-03-30T18:44:20.313 回答
0

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

于 2010-03-30T18:44:08.057 回答
0

@Pyrolistical

since the initial of first static field myclass is not fully constructed ...the result i get is

null null testInitialize.MyObject@70f9f9d8 null

于 2012-04-02T02:27:15.623 回答