【发布时间】:2015-02-24 20:27:37
【问题描述】:
在一个更大的项目中,我遇到了静态字段初始化的奇怪行为(至少就我的理解而言)。据我了解,所有静态字段都应该在程序启动时初始化,这意味着当开始使用非静态字段时,不应该有任何未初始化的静态字段(更准确地说,所有静态赋值“field = .. ." 应该已经执行)。
以下代码不是 MWE,因为它符合我的预期,但它本质上正是我在更大的上下文中所做的。我无法创建一个导致相同问题的较小示例。
运行此代码时:
import java.util.HashSet;
public class FB {
private static final HashSet<String> collection = new HashSet<>();
public static final String foo = bar("item");
public static String bar(String newItem) {
collection.add(newItem);
System.out.println("Yes, I've been invoked, and I currently store this: " + collection);
return newItem;
}
public static void main(String[] args) {
}
}
输出是(因为 Java 首先通过调用 bar(.) 初始化静态字段 'collection' 然后 foo):
Yes, I've been invoked, and I currently store this: [item]
到目前为止,一切都很好。在实际项目中,我正是这样做的(尽管 foo 和 bar(.) 在不同的类中),但是在我实际使用 foo 的值之前不会调用 bar(.)。 (至少这种情况发生在五分之一的情况下——它们都以与上面所示相同的方式创建。其他四个工作正常。)是否有任何情况会导致 Java 出现这样的行为?
我已经看过这些讨论,但它们似乎没有抓住我的问题:
When are static variables are initialized?
Why static fields are not initialized in time?
Java Static Field Initialization
我意识到,当交换 foo 和 collection 的位置时,方法调用无法工作,因为当 foo 被初始化时,collection 不会被初始化(或者更确切地说,初始化为 null?)。 (老实说,我不确定静态字段在不同类中的初始化顺序,所以这可能是问题的根源。)但这会导致
Exception in thread "main" java.lang.ExceptionInInitializerError
而不仅仅是不调用 bar(.)。
如果需要,我可以提供有关实际项目的更多详细信息,但到目前为止我不知道还有什么感兴趣的。 (抱歉,描述的太模糊了,但到目前为止我只有这些。)
【问题讨论】:
-
当
main的第一行被执行时,你对“程序启动”的确切定义是什么? (当然,在那之前可能有代码在运行,包括在所有静态初始化程序运行之前。) -
如果你不能提供最小的重现示例,我建议你使用调试器来查看初始化是如何发生的。
-
我想,我的误解毕竟可能很简单。 Eddie B 写道,如果一个类没有被加载,相应的静态字段也不会被初始化。如果这是真的,它将解释我观察到的行为。我会调查的!
-
嗯,是的,如果 JVM 不加载类,它甚至不知道类中有哪些静态变量。所以任何与静态变量相关的事情都只能在类加载之后发生。
-
是的,这就是问题所在。谢谢大家的支持——我学到了很多! (虽然最后答案很明显。)
标签: java static-initialization