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);
}
}
有人可以告诉我为什么要打印:0 10 10
?我知道它是一个静态的最终变量,它的值不应该改变,但是理解编译器如何初始化字段有点困难。
答案 0 :(得分:6)
这是一个订购问题。静态字段按照遇到的顺序初始化,因此当您调用getc()以初始化alex变量时,尚未设置alex1。你需要首先放置alex1的初始化,然后你才能获得预期的结果。
答案 1 :(得分:4)
其值不是编译时常量表达式的静态final字段按声明顺序初始化。因此,在初始化alex
时,alex1
尚未初始化,因此getc()
会返回默认值alex1
(0
)。
请注意,在以下情况中,结果会有所不同(10 10 10
):
static final int alex1 = 10;
在这种情况下,alex1
由编译时常量表达式初始化,因此它从一开始就被初始化。
答案 2 :(得分:4)
JLS 8.3.2.3“初始化期间使用字段的限制”涵盖了这种情况。
JLS规则允许在您的问题中使用,并声明第一次调用getc()
将返回alex
的默认值(未初始化)。
但是,规则不允许使用未初始化的变量; e.g。
int i = j + 1;
int j = i + 1;
是不允许的。
重新提出其他一些答案。这不是Java编译器“无法弄清楚”的情况。编译器严格执行Java语言规范指定的内容。 (或者换句话说,编译器可以编写以检测示例中的循环,并将其称为编译错误。但是,如果它这样做,它将拒绝有效的Java程序,并且因此,它不是一个符合Java的编译器。)
在评论中你说明了这一点:
...在创建对象之前,必须在编译时或运行时初始化最终字段。
这不正确。
实际上有两种final
字段:
确实在编译时评估了所谓的“常量变量”。 (常量变量是原始类型或类型String的变量“,它是final,并使用编译时常量表达式”初始化 - 请参阅JLS 4.12.4。)。这样的字段将始终在您访问它时进行初始化...模拟某些与此无关的并发症。
其他final
字段按照JLS指定的顺序进行初始化, 可以在初始化之前查看字段的值。对final
变量的限制是它们必须在类初始化期间初始化一次(对于static
)或对象初始化期间。
最后,这个东西是非常“极端情况”的行为。一个典型的写得好的课程不需要
在初始化之前访问final
字段。
答案 3 :(得分:2)
静态字段没有什么特别之处,只是编译器无法使用可以在初始化之前访问字段的方法进行锻炼。
e.g。
public class Main {
private final int a;
public Main() {
System.out.println("Before a=10, a="+getA());
this.a = 10;
System.out.println("After a=10, a="+getA());
}
public int getA() {
return a;
}
public static void main(String... args) {
new Main();
}
}
打印
Before a=10, a=0
After a=10, a=10
答案 4 :(得分:0)
初始化不需要类变量,它们会自动设置为默认值。如果是原语(比如int,short ...),那么对象的{0}(0)为null
。
因此alex1设置为0。
必须初始化方法变量,否则会出现编译错误。
要获得更好的解释,请阅读http://download.oracle.com/javase/tutorial/java/javaOO/classvars.html