在下面的代码中,静态块内的static
变量j
的编译失败,如注释中所述。
但是,它在方法m1()
内运行正常
class StaticBlock {
static {
m1();
//compilation fails because variable are in read indirect state
System.out.println(j);
}
static void m1() {
System.out.println(j);
}
static int j = 10;
我知道编译失败的根本原因-变量j
处于读取间接状态。
我的问题-为什么会这样,我们也可以像在0
中一样在静态块中打印m1()
。
是什么使API开发人员存在这种差异
答案 0 :(得分:1)
为什么会这样,我们也可以在静态块内打印
0
在m1()
中进行。是什么使API开发人员存在这种差异
围绕类初始化期间的事件顺序,常量(即final
)字段字段的一致性,程序员的期望以及易于实现等方面,围绕简单的规范存在着相互竞争的优先级。
常量字段的值提供了一个很好的起点。 Java希望避免观察到此类字段的默认值,尤其是要避免在其他类变量的初始化中使用它们的默认值。因此,在静态初始值设定项块或其他类变量的初始值设定项之前,首先对其进行初始化。它们按照它们在源代码中出现的顺序进行初始化,这是人类和编译器都易于理解的规则。但是,这提供了一个可能性,即一个类变量的初始化程序看到另一个变量的默认值,从而产生令人惊讶的不良结果。因此,Java规定必须在编译时检测并拒绝这种情况。
静态初始化程序块和其他类变量的初始化程序将按照它们在源代码中出现的顺序执行。这里要问的约束条件不那么强,但是通过在此处应用与应用于类常量相同的规则来选择一致性是合理的。结合起来,其效果是易于理解和预测初始化顺序,该顺序也与在评估静态初始值设定项块之前评估和分配的类变量初始值设定项的模型一致。
但是接着是静态方法。非常需要在初始化期间可以使用静态方法,但在初始化顺序注意事项均不相关时,它们也可以在初始化完成后使用。因此,根据源代码中出现的顺序限制静态方法对变量的访问是不可行的。可以想象,可以通过在编译时进行控制流分析或通过某种形式的运行时监视来要求VM跟踪类变量的各个初始化状态,但是Java却不要求这种复杂性,而是选择简单性,允许人们谁坚持这样做(通过观察类变量的默认值)来搞砸。
最后,我强调,所谓的“只读间接写状态”是所有工作方式的第三方模型的一部分。 Java本身没有这样的概念-当涉及到静态方法对类变量的使用要求时,Java正是出于简单性而拒绝这样做的。