静态变量何时初始化?


86

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

另外,如果您可以参考“为什么没有及时初始化静态字段?”这一问题进行解释,尤其是Kevin Brock在同一站点上给出的答案。我不明白第三点。


2
请您编辑您的问题,以包括您所引用的报价。
奥利弗·查尔斯沃思

1
您阅读过Java语言规范吗?故意如此,这是一个易于阅读的文档。如果您已阅读该书,则可能了解发生了什么。如果不是,您可以至少提出一个更具体的问题……
Maarten Bodewes 2012年

我认为这个问答集是stackoverflow.com/questions/3499214的副本。
斯蒂芬·C

Answers:


71

请参阅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
    }
}

这是因为与该类型关联的变量只有一个副本static,而不是与实例变量一样与该类型的每个实例关联的副本,并且如果我们尝试在构造函数中初始化z类型static final,它将尝试重新初始化static final类型字段z因为构造函数是在类的每个实例化上运行的,这些实例化不能在静态final字段中发生。


5
In case of static inner classes, they can not have static fields好像是错字。内部类是非静态的。
Daniel Lubarov 2014年

您应该然而,代替使用虽然
苏拉杰耆那教

当您启动JVM并首次加载类时(这是由以任何方式首次引用该类的类加载器完成的),所有静态块或字段都被“加载”到JVM中并可以访问。
nhoxbypass

1
不幸的是,这个答案包含一些关于何时初始化静态变量的事实错误。请参阅stackoverflow.com/a/3499322/139985
斯蒂芬·C

15

看到:

最后一个特别提供了详细的初始化步骤,这些步骤详细说明了何时初始化静态变量以及初始化的顺序(请注意,final首先初始化为编译时常量的类变量和接口字段)。

我不确定您对第3点的具体问题是什么(假设您是指嵌套的问题?)。详细序列说明这将是递归初始化请求,因此它将继续初始化。


11

当类加载器加载该类时,将初始化静态字段。此时将分配默认值。按照它们在源代码中出现的顺序进行操作。



5

静态变量

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

4

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

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
  }
}

对此类的引用将开始初始化。首先,该类将被标记为已初始化。然后,将使用MyClass()的新实例初始化第一个静态字段。请注意,立即为myClass提供了对空白MyClass实例的引用。有空格,但所有值都为空。现在执行构造函数并打印obj,这是空的。

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

如果这是通过类似以下语句的语句来取消的: MyClass mc = new MyClass();则将再次分配用于新MyClass实例的空间(并将引用放置在中mc)。构造函数将再次执行并再次打印obj现在不为null。

真正的窍门是,当您使用时newWhatEverItIs weii = new WhatEverItIs( p1, p2 ); weii会立即为in提供一个对一些空内存的引用。然后,JVM将继续初始化值并运行构造函数。但是,如果你以某种方式引用weii 之前它这样做-从另一个线程或引用,或通过从类初始化引用,例如-你正在寻找使用null值填充一个类的实例。


1
在初始化完成之前,不会将类标记为已初始化-否则将没有意义。标记为已初始化几乎是最后一步。参见JLS 12.4.2
戴夫牛顿2012年

@DaveNewton:一旦有人引用了该类并开始对其进行初始化,则所有其他引用将把该类视为已初始化。他们不会尝试对其进行初始化,也不会等待其初始化。因此,从程序开始看似非空的字段实际上可以在一段时间内为空。不理解这是造成所有混乱的原因。我认为,最简单的方法是说未初始化的类在第一个引用上被“标记”为已初始化,所有其他引用都将其视为已初始化,这就是这种情况的原因。
RalphChapin 2012年

对先前注释的更正:如Dave Newton的JLS 12.4.2所述,该类在初始化时被锁定。其他线程等待该类被初始化。但是,这并不影响这种情况,它们都在一个线程中发生。
RalphChapin 2012年

4

静态变量可以通过以下三种方式初始化,如下所示,选择任意一种

  1. 您可以在声明时将其初始化
  2. 或者您可以通过制作静态块来实现,例如:

    static {
            // whatever code is needed for initialization goes here
        }
    
  3. 静态块有替代方法-您可以编写私有的静态方法

    class name {
        public static varType myVar = initializeVar();
    
        private static varType initializeVar() {
            // initialization code goes here
        }
    }
    
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.