2

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

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()没有用 new 初始化HashMap

如果我将声明更改map为最终的,那么我会看到它已初始化。或者,即使我删除了 static 并将其设为私有类变量,因为private Map<.....>它也可以。

我一直无法弄清楚为什么会发生这种情况。有人可以解释这里发生了什么吗?

4

3 回答 3

5

切换映射和单例实例初始化的顺序。

静态初始化按照它在源代码中遇到的顺序发生。

请参阅JLS 12.4.2 详细初始化过程,步骤 6(final部分)和 9(“订单”部分)。

(单例实现和 ctor 中的静态混杂,单独的问题。)

于 2012-06-15T23:22:28.390 回答
2

引自http://javapapers.com/core-java/explain-the-final-keyword-in-java/

声明为 final 且未初始化的变量称为空白 final 变量。一个空白的 final 变量会强制构造函数对其进行初始化。

这就是为什么当声明为 final 时它会被初始化

于 2012-06-15T23:12:12.967 回答
2

补充:订单很重要。将静态地图的声明放在 INSTANCE 的声明之前。Java 编译器在排序方面有点愚蠢......

由于 map 是静态的,因此它在 的所有实例之间共享ErrorLookupProvider。因此,在构造函数中使用它可能是一个错误。如果您创建多个 ErrorLookupProviders,您将多次冗余地添加到地图中。相反,在静态初始化块中初始化它。或者,如果它真的意味着在 的实例之间是独立的ErrorLookupProvider,不要让它成为静态的。

于 2012-06-15T23:19:11.960 回答