为什么我的实例初始化程序块在声明之前引用了一个字段?

Ano*_*aly 7 java instance-initializers

我的理解是你不能在声明变量之前引用变量,并且在构造函数创建对象之前,所有代码(包括实例初始值设定项)都在类的主体内但在任何方法之外的顺序执行(例外是static变量和初始化程序块,它们在程序开始时按顺序运行,以初始化整个类).那么,为什么以下代码编译(并运行!):

public class WhyIsThisOk {
    { a = 5; } // why is this ok???
    int a = 10;

    public WhyIsThisOk() {
    }

    public static void main(String[] args) {
        WhyIsThisOk why = new WhyIsThisOk();
        System.out.println(why.a); // 10
    }
}
Run Code Online (Sandbox Code Playgroud)

tso*_*akp 5

文档

Java 编译器将初始化块复制到每个构造函数中。因此,这种方法可用于在多个构造函数之间共享代码块。

上面的说法有点误导,因为如果我们按照上面文档的解释,我们可以像这样重写原始代码:

public class WrongVersionOfWhyIsThisOk {

    int a = 10;

    public WhyIsThisOk (){
        a = 5;
    }

    public static void main(String[] args){
        WrongVersionOfWhyIsThisOk why = new WrongVersionOfWhyIsThisOk ();
        System.out.println(why.a);
    }
}
Run Code Online (Sandbox Code Playgroud)

但是运行WrongVersionOfWhyIsThisOk将产生 5 而不是原始代码产生的 10。

但实际上,初始化块和变量赋值都被复制到构造函数中:

public class RightVersionOfWhyIsThisOk {

    int a;

    public RightVersionOfWhyIsThisOk (){
        a = 5;
        a = 10;
    }

    public static void main(String[] args){
        RightVersionOfWhyIsThisOk why = new RightVersionOfWhyIsThisOk ();
        System.out.println(why.a);
    }
}
Run Code Online (Sandbox Code Playgroud)

更新:

这是详细描述初始化顺序和构造函数调用的文档

4) 执行该类的实例初始化器和实例变量初始化器,将实例变量初始化器的值分配给相应的实例变量,按照它们在类的源代码中以文本形式出现的从左到右的顺序。如果执行这些初始化程序中的任何一个导致异常,则不会处理进一步的初始化程序,并且此过程会突然完成并出现相同的异常。否则,继续第 5 步。

5) 执行此构造函数的其余部分。如果该执行突然完成,那么这个过程也会出于同样的原因突然完成。否则,此过程将正常完成。