我有一个关于初始化的有趣问题。我有以下代码:
public class ErrorLookupProvider {
private static final ErrorLookupProvider INSTANCE = new ErrorLookupProvider();
private static Map<Long, List<String>> map = new HashMap<Long, List<String>>();
private ErrorLookupProvider() {
init();
}
private void init() {
map.put(123L, ImmutableList.of("abc", "def"));
}
public static ErrorLookupProvider getInstance() {
return INSTANCE;
}
}
现在,当我致电ErrorLookupProvider.getInstance()
时,我遇到了NPE。 init()
内的地图未使用新的HashMap
初始化。
如果我将map
的声明更改为final,那么我会看到它已初始化。或者,即使我删除静态并将其作为private Map<.....>
的私有类变量也可以。
我无法弄清楚为什么会这样。有人能解释一下这里发生了什么吗?
答案 0 :(得分:5)
切换地图和单例实例初始化的顺序。
静态初始化按源在源中遇到的顺序发生。
请参阅JLS 12.4.2 Detailed Initialization Procedure,第6步(final
部分)和第9步(“订单”部分)。
(在单独的问题中使用单例实现和静态修改。)
答案 1 :(得分:2)
引自http://javapapers.com/core-java/explain-the-final-keyword-in-java/
声明为final且未初始化的变量称为空白最终变量。空白的最终变量会强制构造函数初始化它。
这就是为什么当宣布最终时它被初始化
答案 2 :(得分:2)
增加:订单很重要。在声明INSTANCE之前,为静态地图声明。关于订购,Java编译器有点愚蠢...
由于map是静态的,因此它在ErrorLookupProvider
的所有实例之间共享。因此,在构造函数中使用它可能是一个错误。如果您创建多个ErrorLookupProviders,您将多次冗余地添加到地图。而是在静态初始化程序块中初始化它。或者,如果它真的意味着在ErrorLookupProvider
的实例之间独立,则不要使其成为静态。