在Java中初始化静态final字段

Ale*_*exx 6 java final field initialization

public class Main {
 static final int alex=getc();
 static final int alex1=Integer.parseInt("10");
 static final int alex2=getc();

public static int getc(){
    return alex1;
}

public static void main(String[] args) {
    final Main m = new Main();
    System.out.println(alex+" "+alex1 +" "+alex2);
  } 
}
Run Code Online (Sandbox Code Playgroud)

有人能告诉我为什么会这样打印:0 10 10?我知道它是一个静态的最终变量,它的值不应该改变,但是理解编译器如何初始化字段有点困难.

ste*_*vls 6

这是一个订购问题.静态字段按照遇到的顺序初始化,因此当您调用getc()以初始化alex变量时,尚未设置alex1.你需要先放置alex1的初始化,然后你才能获得预期的结果.


Ste*_*n C 5

JLS 8.3.2.3 “初始化期间对字段使用的限制”涵盖了这种情况。

JLS 规则允许在您的问题中使用,并声明第一次调用getc()将返回alex.

但是,规则不允许使用未初始化的变量;例如

int i = j + 1;
int j = i + 1;
Run Code Online (Sandbox Code Playgroud)

被禁止。


重新回答一些其他的答案。这不是 Java 编译器“无法弄清楚”的情况。编译器严格执行 Java 语言规范指定的内容。(或者换一种说法,编译器被写入到检测圆你的榜样,并把它编译错误。但是,如果这样做,将被拒绝有效的Java程序,因此不会成为符合Java 编译器。)


在评论中,您说明了这一点:

... final 字段总是必须在编译或运行时在对象创建之前初始化。

这是不正确的。

实际上有两种final字段:

  • 确实在编译时评估了所谓的“常量变量”。(常量变量是“原始类型或字符串类型的变量,它是最终的并使用编译时常量表达式进行初始化” - 参见 JLS 4.12.4。)。这样的字段在您访问它时将始终被初始化......对此处不相关的某些复杂情况进行模数。

  • 其他final字段由JLS指定的顺序初始化,因此,可以看到该字段的值已初始化之前。上的限制final变量是它们必须被初始化一次且仅一次类初始化(一static)或对象初始化。


最后,这个东西是非常“角落案例”的行为。典型的编写良好的类final在初始化之前不需要访问字段。