加载类时未初始化静态变量

时间:2012-06-15 23:02:29

标签: java initialization static-members

我有一个关于初始化的有趣问题。我有以下代码:

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<.....>的私有类变量也可以。

我无法弄清楚为什么会这样。有人能解释一下这里发生了什么吗?

3 个答案:

答案 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的实例之间独立,则不要使其成为静态。