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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/2547713/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-10-29 21:43:34  来源:igfitidea点击:

Why static fields are not initialized in time?

javastaticnullnullpointerexception

提问by Tom Brito

The following code prints nullonce.

以下代码打印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) {}
}

Why are the static objects not initialized before the constructor runs?

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

Update

更新

I'd just copied this example program without attention, I thought we were talking about 2 Object fields, now I saw that the first is a MyClass field.. :/

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

采纳答案by Pyrolistical

Because statics are initialized in the order they are given in source code.

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

Check this out:

看一下这个:

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

That will print:

这将打印:

null
null
myClassObject
null

EDIT

编辑

Ok let's draw this out to be a bit more clear.

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

  1. Statics are initialized one by one in the order as declared in the source code.
  2. Since the first static is initialized before the rest, during its initialization the rest of the static fields are null or default values.
  3. During the initiation of the second static the first static is correct but the rest are still null or default.
  1. 静态按源代码中声明的顺序一一初始化。
  2. 由于第一个静态在其余部分之前初始化,因此在其初始化期间,其余静态字段为空或默认值。
  3. 在启动第二个静态期间,第一个静态是正确的,但其余的仍然为空或默认。

Is that clear?

明白了吗?

EDIT 2

编辑 2

As Varman pointed out the reference to itself will be null while it is being initialized. Which makes sense if you think about it.

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

回答by Kevin Brock

Let's try a different way to explain this...

让我们尝试用不同的方式来解释这个……

This is the sequence the JVM goes through when you first reference the class MyClass.

这是当您第一次引用类时 JVM 经历的序列MyClass

  1. Load the byte-code into memory.
  2. Memory for the static storage is cleared (binary zero).
  3. Initialize the class:
    1. Execute each static initializer in the order that it appears, this includes static variables and static { ... }blocks.
    2. JVM then initializes your myClassstatic variable to a new instance of MyClass.
    3. When this happens, the JVM notices that MyClassis already loaded (byte-code) and in the process of being initialized, so it skips initialization.
    4. Allocate memory on heap for object.
    5. Execute constructor.
    6. Print out value of objwhich is still null(since it is not part of the heap and constructor initialized variables).
    7. When constructor finishes, execute next static initializer which sets objto a new instance of Object.
  4. Class initialization done. From this point, all constructor calls will behave as you presume/expect - that is objwould not be nullbut a reference to an Objectinstance.
  1. 将字节码加载到内存中。
  2. 静态存储的内存被清除(二进制零)。
  3. 初始化类:
    1. 按照出现的顺序执行每个静态初始化程序,这包括静态变量和static { ... }块。
    2. JVM 然后将您的myClass静态变量初始化为MyClass.
    3. 发生这种情况时,JVM 会注意到MyClass已经加载(字节码)并且正在初始化的过程中,因此它会跳过初始化。
    4. 在堆上为对象分配内存。
    5. 执行构造函数。
    6. 打印出它的值obj仍然是null(因为它不是堆和构造函数初始化变量的一部分)。
    7. 构造函数完成后,执行下一个静态初始化程序,它设置obj为 的新实例Object
  4. 类初始化完成。从这一点来看,所有构造函数调用都将按照您的假设/期望进行操作 - 这obj不会null只是对Object实例的引用。

Remember that Java specifies that a finalvariable is assigned a value once. It is not that it is guaranteed to be assigned a value when the code references it unless you ensure that the code references it after it is assigned.

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

This is not a bug. This is the defined way to handle usage of the class during its own initialization. If this were not so, then the JVM would go into an infinite loop. See step #3.3 (if the JVM does not skip initialization for a class that is in the process of initialization it would just keep initializing it - infinite loop).

这不是错误。这是在其自身初始化期间处理类使用的已定义方法。如果不是这样,那么 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.

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

回答by Slava Imeshev

That's because Java executes the static section in order it is declared. In your case, the sequence is

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

  1. new MyClass
  2. new Object
  1. 新的我的课堂
  2. 新对象

When #1 is executed, obj is still not initialized, so it prints null. Try the following and you will see the difference:

执行#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
  }
}

Generally speaking, it is better to avoid such a construct all together. If you are trying to create a singleton, that's how that code fragment should look like:

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

class MyClass {

  private static final MyClass myClass = new MyClass();

  private Object obj = new Object();

  private MyClass() {
    System.out.println(obj); // will print null once
  }
}

回答by iamx7777777

@Pyrolistical

@Pyrolistical

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

由于第一个静态字段 myclass 的初始值未完全构建......我得到的结果是

null null testInitialize.MyObject@70f9f9d8 null

null null testInitialize.MyObject@70f9f9d8 null

回答by antony

that is because static fields initialized in same order they defined.

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