简短的回答
当类初始化开始时,k
初始值为 0。
然后执行静态块(因为它在声明中的赋值之前),k
并将被赋值为 2。
然后声明中的初始化器被执行,并被k
赋值为 1。
长解释
让我们使用这个例子,因为你的例子有点简单:
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,从第 4.12.5 节开始:
程序中的每个变量在使用它的值之前都必须有一个值:
- 每个类变量、实例变量或数组组件在创建时都使用默认值进行初始化
(规范中的以下几行指定了所有类型的默认值,基本上是某种形式的 0,例如0
、0.0d
、null
、false
等)
所以在类被初始化之前(由于这些原因之一),变量将保持一个初始值。
根据详细的初始化过程(这里只引用有趣的步骤,并强调我的):
6. [...] 然后,初始化其值为编译时常量表达式的接口的final
类变量和字段(第 8.3.2.1 节、第 9.3.1 节、第 13.4.9 节、第 15.28 节)。
[...]
9. 接下来,按照文本顺序执行类的类变量初始化程序和静态初始化程序,或者接口的字段初始化程序,就好像它们是一个单独的块一样。
让我们看一下第 6 步,使用 4 个final
类变量:stat1
、str2
、second
、lazy
。
由于10
是常量表达式, 也是"sdfff"
,并且由于执行顺序,不可能观察str2
和的初始值stat1
。为了进行观察,您最早可以在步骤 9 中进行。
案例second
证明当右手边不是编译时常量表达式时,所以它的初始值是可见的。
的情况lazy
不同,因为分配是在静态块中完成的,因此发生在第 9 步 - 因此可以观察其初始值。(好吧,编译器会仔细检查lazy
只分配了一次)。
在使用编译时常量表达式初始化最终类变量之后,执行静态块和其余的初始化程序。
从示例中可以看出,静态块和初始化是根据文本顺序进行的 - 使用str
变量进行演示 - 首先打印为null
, 然后something
, 然后crap
。