【问题标题】:Why static fields are not initialized in time?为什么静态字段没有及时初始化?
【发布时间】:2011-02-02 14:42:24
【问题描述】:

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

【问题讨论】:

    标签: java static null nullpointerexception


    【解决方案1】:

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

    看看这个:

    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 指出的那样,在初始化时,对自身的引用将为空。如果您考虑一下,这是有道理的。

    【讨论】:

    • ..因为myClass 本身是静态的。
    • 好的,按顺序初始化静态。而且静态在构造函数之前,为什么构造函数运行时没有初始化呢?对我来说真的像一个错误..
    • @Tom,不,你弄错了。静态不在构造函数之前。当分别调用构造函数时,静态是初始化的。在我的示例中,当第一个静态是使用 MyClass 初始化时,将调用构造函数。当构造函数运行时 myClass 是 init (因为它自己运行),但 myClass2 不是。当第二个 MyClass 是 init 时,它再次调用构造函数,这次 myClass 已经 init 并且 myClass2 那时正在 init
    • 所以在构造函数运行时,它的静态变量在另一个线程中初始化?您是否有指向相同文本的链接,详细说明了这一点?
    • @Pyrolistical :当我执行你的程序时,我得到了不同的结果。它打印了null null myClassObjectref null
    【解决方案2】:

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

    这是当您第一次引用类 MyClass 时 JVM 执行的顺序。

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

    请记住,Java 指定 final 变量被赋值一次。不是保证在代码引用它的时候就一定给它赋值,除非你保证在赋值之后代码引用它。

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

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

    【讨论】:

    • 很好的答案凯文,这里最好的一个
    • 你的意思是它跳过了“静态”初始化,我想。
    【解决方案3】:

    这是因为 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
      }
    }
    

    【讨论】:

    • 更正了 的执行顺序。不过,Singleton 并不是真正的 Singleton,因为构造函数是公共的。
    • 好的,按顺序初始化静态。并且静态是在构造函数之前声明并要求初始化的,那么为什么在构造函数运行时它没有初始化呢?对我来说真的像一个错误..
    • @Tom 你必须通过调用 new MyClass() 来理解你调用构造函数的静态
    • 认为我们的通信失败了。我实际上知道 new MyClass() 是对构造函数的调用,这并不能解释为什么构造函数运行时静态字段为空。就像实例字段在构造函数之前初始化一样,静态字段应该是……但为什么不呢?
    • 静态成员 在实例构造函数运行之前构造。只是在你的代码中,你的静态初始化器调用你的构造器。这是一个先有鸡还是先有蛋的问题。
    【解决方案4】:

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

    【讨论】:

    • 这没有回答为什么它在构造函数时为空。
    【解决方案5】:

    @Pyrolistical

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

    空 空值 testInitialize.MyObject@70f9f9d8 空

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2011-09-15
      • 1970-01-01
      • 2012-03-10
      • 1970-01-01
      • 2012-01-07
      • 2011-01-19
      • 2014-10-06
      相关资源
      最近更新 更多