1

假设我有一堂课

class A{
    public final static TreeMap<String,String> tmap = new TreeMap<>();
    int x;

    static{
        tmap.put("x:I", "Hello");
    }
}

我创建了一个子类

class B extends A{
    long y;

    static{
        tmap.put("y:J","World");
    }
}

如果我现在编写一些代码来检查静态初始化程序:

class Main{
    public static void main (String[] args){
        B b = new B();
        for(String v : b.tmap.values()){
            System.out.println(v);
        }
    }
}

我知道这两个条目都必须在,tmap因为A最终必须加载,因为B's super call 最迟。

但是,如果我正在阅读静态类初始化何时发生?正确地,我不能假设该Hello值总是首先放入地图中,因为tmap它是最终的。

因此,如果排序很重要(例如,如果我知道某些值可能会在层次结构中进一步更新/覆盖),我是否需要删除final修饰符?

还是有其他东西已经强制执行“正确的”静态初始化程序排序?

4

1 回答 1

1

静态初始化总是在子类初始化之前为父类完成,如果你有多个静态初始化块,它们会按顺序执行。(编辑: 感谢 Holger 指出在加载类时不必直接这样做)。

在您的示例中,B 扩展了 A,因此类加载器必须在 B 之前加载 A。因此,首先执行 A 的静态初始化程序。

这与 final 修饰符无关。另一个线程可能指的是,如果编译器可以执行常量折叠(https://www.javaworld.com/article/2076060/build-ci-sdlc/compiler-optimizations.html),则常量来自的类将不会被静态初始化(因为引用在编译时被替换)。

于 2018-01-23T15:00:29.373 回答