何时初始化静态变量?

Ank*_*kit 74 java static initialization

我想知道什么时候静态变量初始化为默认值.是否正确加载类时,会创建(分配)静态变量,然后执行声明中的静态初始化和初始化?在什么时候给出默认值?这导致了前向参考的问题.

另外请您解释一下这个问题,为什么静态字段没有及时初始化?特别是Kevin Brock在同一网站上给出的答案.我无法理解第三点.

Lio*_*ion 61

查看Java静态变量方法:

  • 它是属于类的变量而不是对象(实例)
  • 静态变量仅在执行开始时初始化一次.在初始化任何实例变量之前,将首先初始化这些变量
  • 要由类的所有实例共享的单个副本
  • 静态变量可以由类名直接访问,不需要任何对象.

如果您无法有目的地初始化它们,则实例和类(静态)变量会自动初始化为标准默认值.虽然局部变量不会自动初始化,但您无法编译无法初始化局部变量或在使用之前为该局部变量赋值的程序.

编译器实际上做的是在内部生成单个类初始化例程,该例程将所有静态变量初始化器和所有静态初始化器代码块按它们在类声明中出现的顺序组合在一起.首次加载类时,此单个初始化过程仅自动运行一次.

对于内部类,它们不能具有静态字段

一个内部类是没有明确或隐含声明的嵌套类static.

...

内部类可能不会声明静态初始化器(第8.7节)或成员接口......

内部类可能不会声明静态成员,除非它们是常量变量...

请参阅JLS 8.1.3内部类和封闭实例

finalJava中的字段可以与其声明位置分开初始化,但这不适用于static final字段.请参阅下面的示例.

final class Demo
{
    private final int x;
    private static final int z;  //must be initialized here.

    static 
    {
        z = 10;  //It can be initialized here.
    }

    public Demo(int x)
    {
        this.x=x;  //This is possible.
        //z=15; compiler-error - can not assign a value to a final variable z
    }
}
Run Code Online (Sandbox Code Playgroud)

这是因为只有一个与该类型相关的变量副本static,而不是与实例变量相关的每个类型实例,如果我们尝试在构造函数中初始化z类型static final,它将尝试重新初始化static final类型字段z因为构造函数是在类的每个实例化上运行的,静态final字段不得出现.

  • `在静态内部类的情况下,它们不能有静态字段`似乎是一个错字.内部类是非静态的. (5认同)

Dav*_*ton 14

看到:

最后一个特别提供了详细的初始化步骤,这些步骤在静态变量初始化时说明,并且按什么顺序排列(需要注意的final是首先初始化类变量和编译时常量的接口字段.)

我不确定你对第3点的具体问题(假设你的意思是嵌套的?)是.详细的序列表明这将是一个递归初始化请求,因此它将继续初始化.


Dav*_*ave 8

当类加载器加载类时,将初始化静态字段.此时会分配默认值.这是按顺序完成的,而不是源代码中显示的顺序.


Ósc*_*pez 7

初始化的顺序是:

  1. 静态初始化块
  2. 实例初始化块
  3. 构造函数

JVM 规范文档中解释了该过程的详细信息.


ale*_*oot 6

静态变量

  • 它是属于类而不属于对象(实例)的变量
  • 静态变量仅在执行开始时初始化一次(当类加载器第一次加载类时)。
  • 在初始化任何实例变量之前,将首先初始化这些变量
  • 由类的所有实例共享的单个副本
  • 静态变量可以通过类名直接访问,不需要任何对象


Ral*_*pin 5

从另一个问题的代码开始:

class MyClass {
  private static MyClass myClass = new MyClass();
  private static final Object obj = new Object();
  public MyClass() {
    System.out.println(obj); // will print null once
  }
}
Run Code Online (Sandbox Code Playgroud)

对该类的引用将开始初始化。首先,该类将被标记为已初始化。然后第一个静态字段将使用 MyClass() 的新实例进行初始化。请注意,myClass 立即获得对空白MyClass 实例的引用。空间存在,但所有值均为空。现在执行构造函数并打印obj,它为 null。

现在回到初始化类: obj引用一个新的真实对象,我们就完成了。

如果这是由如下语句引发的: MyClass mc = new MyClass();再次分配新 MyClass 实例的空间(并将引用放置在 中mc)。构造函数再次执行并再次打印obj现在它不为空。

这里真正的技巧是,当您使用new, as inWhatEverItIs weii = new WhatEverItIs( p1, p2 ); weii时,会立即给出对空内存位的引用。然后 JVM 将继续初始化值并运行构造函数。但是,如果您以某种方式weii 它执行此操作之前进行引用(例如,通过从另一个线程引用它或通过从类初始化中引用它),您将看到一个充满空值的类实例。