这是一些在尚未初始化的类上调用静态方法A.f()的代码。 有人可以用JLS来解释这段代码的行为吗?
class A {
final static Object b = new B();
final static int S1 = 1;
final static Integer S2 = 2;
static void f() {
System.out.println(S1);
System.out.println(S2);
}
}
class B {
static {
A.f();
}
}
public class App
{
public static void main( String[] args )
{
A.f();
}
}
输出:
1
null
1
2
答案 0 :(得分:29)
A.f()
中的 App.main()
会触发类A
的初始化。
初始化所有常量变量。唯一的常量变量是S1
,现在是1
。
然后,其他静态字段按文本顺序初始化。 b
是第一个字段,它触发类B
的初始化,后者又调用A.f()
。 S2
只是null
,因为它尚未初始化。 b
的初始化现已完成。最后但并非最不重要的是,S2
已初始化为Integer
对象2
。
S2
不是常量变量,因为它不是基本类型int
,而是引用类型Integer
。 S2 = 2;
是S2 = Integer.valueOf(2);
的自动装箱速记。
如果字段声明中的声明符具有变量初始值设定项,则声明符具有声明变量的赋值(第15.26节)的语义。
[...]
请注意,作为常量变量的
static
字段(第4.12.4节)在其他static
字段之前初始化(第12.4.2节)。这也适用于接口(第9.3.1节)。永远不会观察到这些字段具有默认的初始值(§4.12.5),即使是狡猾的程序也是如此。
常量变量是基本类型或类型
final
的{{1}}变量,用常量表达式(第15.28节)初始化。变量是否是常量变量可能对类初始化(第12.4.1节),二进制兼容性(第13.1节,第13.4.9节)和明确赋值(第16节(定义赋值))有影响。
常量表达式是表示基本类型值的表达式,或者是
String
不突然完成的值,仅使用以下内容组成:
- 原始类型的文字和
类型的文字String
[...]
对于每个类或接口C,都有一个唯一的初始化锁
String
。从C到LC
的映射由Java虚拟机实现决定。初始化C的过程如下:[...]
- 醇>
否则,记录当前线程正在进行C的
LC
对象初始化的事实,并释放Class
。然后,初始化C的
LC
字段,它们是常量变量(§4.12.4,§8.3.2,§9.3.1)。[...]
- 接下来,按文本顺序执行类的类变量初始值设定项和静态初始值设定项,或接口的字段初始值设定项,就好像它们是单个块一样。
醇>
12.4.2. Detailed Initialization Procedure
程序中的每个变量在使用其值之前必须具有值:
每个类变量,实例变量或数组组件在创建时都使用默认值进行初始化(§15.9,§15.10.2):
[...]
- 对于所有参考类型(§4.3),默认值为
static
。
答案 1 :(得分:2)
此问题似乎不属于JLS
,但我们处理JVMS
。
在Linking
进程之前的resolution
阶段,有Preparation次级,其中包括:
为类或接口创建静态字段并初始化 这些字段为默认值
以及更多:
静态字段的显式初始化程序作为其一部分执行 初始化(第5.5节),而不是准备
虽然initialization包括:
执行任何一个Java虚拟机指令new
初始化基元类型包括写入它们的初始值。对于引用类型字段,它们的默认值为null
,因为在Resolution之前,子格jvm不“知道”哪个类与类的适当符号引用名相关联。