我有以下类:
public class A {
static {
B.load(A.class);
}
public static final C field1 = new C("key1", "value1");
public static final C field2 = new C("key2", "value2");
public static void main(String[] args) {
System.out.println(A.field1);
}
}
和
public class B {
public static void load(Class<?> clazz) {
for (Field field : clazz.getFields()) {
try {
System.out.println("B.load -> field is " + field.get(null));
} catch (IllegalArgumentException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
}
}
和
public class C {
private final String key;
private final String value;
public C(String key, String value) {
super();
this.key = key;
this.value = value;
}
public String getKey() {
return this.key;
}
public String getValue() {
return this.value;
}
@Override
public String toString() {
return "C [key=" + this.key + ", value=" + this.value + "]";
}
}
当A被执行时,我得到:
B.load -> field is null
B.load -> field is null
C [key=key1, value=value1]
为什么field.get(null)在执行时返回空值?
我相信所有静态成员都将按照声明顺序执行(对于静态块)和初始化(对于静态字段)。尝试将类A
的静态块放在类的末尾,或者至少放在静态字段之后。让我知道这是否有影响。
编辑:关于原语和字符串行为的信息…
当final静态字段是一个原语或String字面值(或一个可以静态求值的表达式,结果是其中之一)时,它被认为是一个编译时常数。基本上,设置这样的值不需要"计算",比如调用构造函数或对可能尚未初始化的其他字段求值。虽然字符串不是原语,但在编译时对它们进行了特殊处理,使代码中的字符串字面值成为可能。
这意味着这样的字段可以在类加载并准备初始化时立即分配。虽然我不知道规范的细节,但是用反射的实验表明这是必须发生的。
Java语言规范的以下部分与理解此行为相关:
- 初始化时对字段使用的限制。反射使您能够比它们在源代码中出现的顺序"更快"地获得这些字段。如果你试图在字段声明之前的静态块中直接使用它们,你会得到关于编译器非法前向引用的错误。
- 初始化类和接口。 常量表达式的构成
- 最终变量。这一节的最后一段是相当相关的,暗示了比它的模糊让更多。
在类A中,必须在调用静态函数之前声明静态字段
public static final C field1 = new C("key1", "value1");
public static final C field2 = new C("key2", "value2");
static {
B.load(A.class);
}
Java教程解释:
A class can have any number of static initialization blocks, and they can appear anywhere in the class body. The runtime system guarantees that static initialization blocks are called in the order that they appear in the source code.