java 为什么我的示例初始化器块可以在声明字段之前引用它?

pinkon5k  于 2023-01-01  发布在  Java
关注(0)|答案(5)|浏览(135)

我的理解是,在变量被声明之前,您不能引用它,并且所有代码(包括示例初始化式),它位于类的主体内,但在任何方法之外,在创建对象时按构造函数之前的顺序执行(例外是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
    }
}
qyzbxkaa

qyzbxkaa1#

docs开始:
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);
    }
}

但是运行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);
    }
}
    • 更新日期:**

下面是详细描述初始化顺序和构造函数调用的文档:
4)执行此类的示例初始值设定项和示例变量初始值设定项,将示例变量初始值设定项的值按它们在类的源代码中文本显示的从左到右的顺序分配给相应的示例变量。如果执行这些初始值设定项中的任何一个导致异常,则不再处理其他初始值设定项,并且此过程以相同的异常突然完成。否则,则继续步骤5。
5)执行这个构造函数体的其余部分。如果那个执行突然结束,那么这个过程也会因为同样的原因突然结束。否则,这个过程正常完成。

bcs8qyzn

bcs8qyzn2#

来自文档:

8.3.2.3.初始化期间字段使用的限制

只有当成员是类或接口C的示例(分别为静态)字段,并且满足以下所有条件时,成员的声明才需要在使用之前以文本形式出现:

  • 这种用法出现在C的示例(分别为静态)变量初始化器或示例(分别为静态)初始化器中

C.

  • 用法不在分配的左侧。
  • 用法是通过一个简单的名称。
  • C是包含用法的最内层类或接口。
    • 如果上述四个要求中的任何一个未得到满足,则会出现编译时错误**

在本例中,用法位于赋值语句的左侧,因此这不是编译时错误。

woobm2wo

woobm2wo3#

初始化器块的内容在任何构造函数被调用时执行(在构造函数的内容之前)。
因此,您可以提供对任何变量的引用,因为除非调用构造函数(即创建对象),否则不会使用这些变量。

lzfw57am

lzfw57am4#

示例初始化块在示例创建时调用time.so,创建why对象后,它正常工作。
初始化的顺序为:
1.静态阻塞
1.构造器
1.示例块按出现顺序排列

2w2cym1i

2w2cym1i5#

声明的顺序并不重要,您也可以这样写:

public  class WhyIsThisOk {

    {
        a = 5;
    }

    public WhyIsThisOk() {
    }

    public static void main(String[] args) {
        System.out.println(new WhyIsThisOk().a);
    }

    int a = 10;
}

重要的是,编译器首先复制(自上而下)a=5,然后将a=10复制到构造函数中,这样看起来就像:

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

最后看这个例子:

public class WhyIsThisOk {

    {
        a = get5();
    }

    public WhyIsThisOk() {
        a = get7();
    }

    public static void main(String[] args) {
        System.out.println(new WhyIsThisOk().a);
    }

    int a = get10();

    public static int get5() {
        System.out.println("get5 from: " + new Exception().getStackTrace()[1].getMethodName());
        return 5;
    }

    public static int get7() {
        System.out.println("get7 from: " + new Exception().getStackTrace()[1].getMethodName());
        return 7;
    }

    public static int get10() {
        System.out.println("get10 from: " + new Exception().getStackTrace()[1].getMethodName());
        return 10;
    }
}

输出为:

get5 from: <init>
get10 from: <init>
get7 from: <init>
7

相关问题