静态变量初始化的顺序,Java

2022-09-04 03:03:01

可能的重复:
Java静态类初始化
以什么顺序执行类中的静态块和静态变量?

当我运行此代码时,答案是1,我以为会是2。初始化的顺序和每个步骤中的 k 值是什么?

public class Test {

    static {k = 2;}
    static int k = 1;

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

编辑1:作为“k设置为默认值”的后续,那么为什么下一个代码无法编译?出现错误“在定义字段之前无法引用字段”。

public class Test {

    static {System.out.println(k);}
    static int k=1;

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

编辑2:对于一些我不知道的原因,它^在“k”而不是“Test.k”时起作用。

感谢所有的答案。这将:D


答案 1

它们按照您编写的顺序执行。如果代码是:

public class Test {

    static int k = 1;
    static {k = 2;}

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

}

则输出变为 2。

初始化的顺序为:.。类的类变量初始值设定项和类的静态初始值设定项...,按文本顺序排列,就好像它们是单个块一样。

值(对于您的代码)是:k = 0(默认值),然后将其设置为 2,然后将其设置回 1。

您可以通过运行以下代码来检查它是否实际设置为 2:

private static class Test {

    static {
        System.out.println(Test.k);
        k = 2;
        System.out.println(Test.k);
        }
    static int k = 1;

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

答案 2

简短的回答

当类的初始化开始时,初始值为 0。k

然后执行静态块(因为它在声明中的赋值之前),并将被赋值 2。k

然后执行声明中的初始值设定项,并将分配 1。k

长篇解释

让我们使用这个例子,因为你的例子有点简单:

class TestInitOrder {
  static {
    System.out.println(TestInitOrder.stat1);
    System.out.println(TestInitOrder.stat2);
    System.out.println(TestInitOrder.str);
    System.out.println(TestInitOrder.str2);

    str = "something";

    System.out.println(TestInitOrder.str);
    System.out.println(TestInitOrder.str2);
    System.out.println(TestInitOrder.lazy);
    System.out.println(TestInitOrder.second);
  }

  private static final int stat1 = 10;
  static final String str2 = "sdfff";
  static String str = "crap";
  private static int stat2 = 19;
  static final Second second = new Second();
  static final int lazy;

  static {
    lazy = 20;
  }

  static {
    System.out.println(TestInitOrder.str2);
    System.out.println(TestInitOrder.stat2);
    System.out.println(TestInitOrder.str);
    System.out.println(TestInitOrder.lazy);
    System.out.println(TestInitOrder.second);
  }

  public static void main(String args[]) {
  }

}

class Second {
  public Second() {
    System.out.println(TestInitOrder.second);
  }
}

根据 Java 语言规范,从 4.12.5 节中:

程序中的每个变量在使用其值之前都必须有一个值:

  • 每个类变量、实例变量或数组组件在创建时都使用默认值进行初始化

(规范中的以下行指定了所有类型的默认值,基本上是某种形式的 0,例如 、 、 、 等。00.0dnullfalse

因此,在初始化类之前(由于这些原因之一),变量将保存初始值。

根据详细的初始化过程(这里只引用了有趣的步骤,并强调我的):

6. [...]然后,初始化其值为编译时常量表达式的接口的最终类变量和字段 (§8.3.2.1, §9.3.1, §13.4.9, §15.28)。

[...]

9. 接下来,按文本顺序执行类的类变量初始值设定项和静态初始值设定项,或接口的字段初始值设定项,就好像它们是单个块一样。

让我们看一下步骤 6,其中包含 4 个类变量:、 、 、 。finalstat1str2secondlazy

由于 是常量表达式,因此是 ,并且由于执行顺序,因此无法观察到 和 的初始值。为了进行观察,您可以做的最早是在步骤9中。10"sdfff"str2stat1

演示的情况是,当右侧不是编译时常量表达式时,因此其初始值是可见的。second

的情况是不同的,因为赋值是在静态块中完成的,因此发生在步骤9中 - 因此可以观察其初始值。(好吧,编译器会仔细检查只分配一次)。lazylazy


在使用编译时常量表达式初始化最终类变量之后,将执行静态块和初始值设定项的其余部分。

从示例中可以看出,静态块和初始化根据文本顺序进行 - 通过使用变量进行演示 - 它首先打印出来为,然后,然后。strnullsomethingcrap


推荐