解释 java 编译顺序

explain java compile order

v2第一次进入A的c'tor时是null,但是如果我把v2的声明&初始化放在instance之前它就会有值;这是为什么?

public class A {

    private static final String v1 = new String(new byte[]{'a', 'b'});

    private static A instance = new A();

    private static final String v2 = new String(new byte[]{'b', 'c'});

    private A() {
        System.out.printf("A c'tor\tv1 [%s]\tv2 [%s]\n", v1, v2);
    }

    public static void main(String[] args) {
        System.out.println("start main");
        new A();
        System.out.println("end main");
    }
}

输出:

    A c'tor v1 [ab] v2 [null]
    start main
    A c'tor v1 [ab] v2 [bc]
    end main

此外,如果我将 v2 的初始化更改为:

private static final String v2 = "ab";

它确实初始化了 v2,输出是:

A c'tor v1 [ab] v2 [ab]
start main
A c'tor v1 [ab] v2 [ab]
end main

编辑

第二部分的另一个测试:

public class A {

    private static final String v1 = new String(new byte[]{'a', 'b'});

    private static transient A instance = new A();

    private static final String v2 = new String(new byte[]{'b', 'c'});
    private static final String v3 = new String("ab");
    private static final String v4 = "ab";

    private A() {
        System.out.printf("A c'tor\tv1 [%s] v2 [%s] v3 [%s] v4 [%s]\n", v1, v2, v3, v4);
    }

    public static void main(String[] args) {
        System.out.println("start main");
        new A();
        System.out.println("end main");
    }
}

输出:

A c'tor v1 [ab] v2 [null] v3 [null] v4 [ab]
start main
A c'tor v1 [ab] v2 [bc] v3 [ab] v4 [ab]
end main

当 class A 初始化时(在执行 main 之前),静态变量按照它们在源文件中出现的顺序进行初始化。

  • 第一个 v1
  • then instance(打印 A c'tor v1 [ab] v2 [null] 因为它的初始化涉及创建 A 的实例)- 它在 v2 之前初始化,这就是为什么 v2 仍然是 null.
  • 然后是 v2

初始化后,main 方法被执行,并产生接下来的 3 行输出。在 main 方法中创建 A 的新实例会产生与先前构造函数调用不同的输出,因为此时 v1v2 都已初始化。

编辑:

关于您更新的问题:

如果您关注 initialization procedure in JLS 12.4.2:

  1. Otherwise, record the fact that initialization of the Class object for C is in progress by the current thread, and release LC. Then, initialize the final class variables and fields of interfaces whose values are compile-time constant expressions.

...

  1. Next, execute either the class variable initializers and static initializers of the class, or the field initializers of the interface, in textual order, as though they were a single block.

如您所见,值为 compile-time constant expressions 的最终静态变量在其余静态变量之前被初始化。因此,将 v2 的值更改为常量 "ab" 会导致 v2instance 变量之前初始化,这解释了不同的输出。