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

Posted

技术标签:

【中文标题】为啥静态字段没有及时初始化?【英文标题】: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 字段.. :/

【问题讨论】:

【参考方案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

编辑

好的,让我们把它画得更清楚一点。

    静态数据按照源代码中声明的顺序一一初始化。 由于第一个静态字段在其余静态字段之前初始化,因此在初始化期间,其余静态字段为空值或默认值。 在第二个静态初始化期间,第一个静态是正确的,但其余的仍然是 null 或默认值。

清楚吗?

编辑 2

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

【讨论】:

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

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

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

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

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

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

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

【讨论】:

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

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

    新的我的班级 新对象

#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 空

【讨论】:

以上是关于为啥静态字段没有及时初始化?的主要内容,如果未能解决你的问题,请参考以下文章

为啥我可以通过反射在字段初始化后重写静态只读字段?

为啥我无法在 C++ 中初始化静态字段 [重复]

C # 如何连接到sql 数据库 要详细的代码,为啥显示字段初始值无法引用非静态字段?!

静态初始化块

静态字段静态函数成员常量

每日问题之子类字段属性使用父类的非静态字段属性错误