静态变量初始化的顺序,Java [重复]

问题描述 投票:10回答:2

可能重复:Java static class initializationin what order are static blocks and static variables in a class executed?

当我运行此代码时,答案为1,我认为应该是2。初始化的顺序和每个步骤中k的值是什么?

public class Test {

    static {k = 2;}
    static int k = 1;

    public static void main(String[] args) {
        System.out.println(k);
    }
}

编辑1:跟进“ k设置为默认值”,那么为什么下一个代码不能编译?错误“在定义字段之前无法引用它”。

public class Test {

    static {System.out.println(k);}
    static int k=1;

    public static void main(String[] args) {
        System.out.println(k);
    }
}

编辑2:由于某些我不知道的原因,它^在代替它的“ Test.k”而不是“ k”时起作用。

感谢所有答案。这将满足:D

java static
2个回答
8
投票

它们按照您编写它们的顺序执行。如果代码是:

public class Test {

    static int k = 1;
    static {k = 2;}

    public static void main(String[] args) {
        System.out.println(k);
    }

}

然后输出变为2。

初始化的顺序是:..the class variable initializers and static initializers of the class..., in textual order, as though they were a single block.

并且(对于您的代码而言)值为:k = 0(默认值),然后将其设置为2,然后将其设置回1。

您可以通过运行以下代码来检查它实际上是否设置为2:

private static class Test {

    static {
        System.out.println(Test.k);
        k = 2;
        System.out.println(Test.k);
        }
    static int k = 1;

    public static void main(String[] args) {
        System.out.println(k);
    }
}

4
投票

简短回答

当类的初始化开始时,k的初始值为0。

然后执行静态块(因为它在声明中的赋值之前),并且将k赋值为2。

然后执行声明中的初始化程序,并将k分配为1。

详细说明

让我们使用this example,因为您的示例有点简单:

class TestInitOrder {
  static {
    System.out.println(TestInitOrder.stat1);
    System.out.println(TestInitOrder.stat2);
    System.out.println(TestInitOrder.str);
    System.out.println(TestInitOrder.str2);

    str = "something";

    System.out.println(TestInitOrder.str);
    System.out.println(TestInitOrder.str2);
    System.out.println(TestInitOrder.lazy);
    System.out.println(TestInitOrder.second);
  }

  private static final int stat1 = 10;
  static final String str2 = "sdfff";
  static String str = "crap";
  private static int stat2 = 19;
  static final Second second = new Second();
  static final int lazy;

  static {
    lazy = 20;
  }

  static {
    System.out.println(TestInitOrder.str2);
    System.out.println(TestInitOrder.stat2);
    System.out.println(TestInitOrder.str);
    System.out.println(TestInitOrder.lazy);
    System.out.println(TestInitOrder.second);
  }

  public static void main(String args[]) {
  }

}

class Second {
  public Second() {
    System.out.println(TestInitOrder.second);
  }
}

根据Java Language Specification,来自section 4.12.5

程序中的每个变量都必须有一个值,然后才能使用它的值:

  • 每个类变量,实例变量或数组组件在创建时均使用默认值初始化

(规范的以下几行指定所有类型的默认值,基本上是0的某种形式,例如00.0dnullfalse等)

因此,在初始化类之前(由于[C​​0]之一,变量将保留一个初始值。

根据these reasons(仅在此处引用有趣的步骤,并强调我的意思:):

6。[...]然后,初始化其值为compile-time constant expressions(第8.3节)的detailed initialization procedure类变量接口的字段。 2.1,§9.3.1,§13.4.9,§15.28)。

[...]

9。接下来,以文本顺序执行类的类变量初始化器和静态初始化器,或接口的字段初始化器,就好像它们是单个块一样。

[让我们看一下第6步,有4个final类变量:finalstat1str2second

因为lazy是常数表达式,10也是如此,并且由于执行顺序,所以不可能观察 "sdfff"str2的初始值。为了进行观察,您最早可以做的是在步骤9中。

stat1的情况表明,当右侧不是编译时常量表达式时,则其初始值可见。

second的情况不同,因为分配是在静态块中完成的,因此发生在步骤9中-因此可以观察其初始值。 (好吧,编译器会仔细检查lazy是否恰好分配了一次)。


使用编译时常量表达式初始化最终类变量后,将执行静态块和其余初始化程序。

如您从示例中看到的,静态块和初始化根据文本顺序进行-通过使用lazy变量进行演示-首先将其打印为str,然后是null,然后是something

© www.soinside.com 2019 - 2024. All rights reserved.