我已经检查了Forward References During Field Initialization和这个the answer from @assylias,但仍然没有得到为什么的答案。
为什么静态块可以分配在其后声明的静态变量,但不能访问它?
class Parent {
static {
i = 2; // valid
// can only assign new value to it instead of accessing it?
// System.out.println(i); // invalid - compile-error
}
static int i = 0;
static {
i = 3; // valid
}
}
是由于以下事实:该值尚未初始化,因此我们只是明确禁止您使用它?还是我不知道与安全相关的东西?
这不是该问题的重复
为什么使用类名访问时不会发生这种情况?
这个问题是关于为什么我们要有这种设计?出于什么目的?
答案 0 :(得分:3)
静态字段根据它们在代码中出现的顺序进行初始化。
因此,当您为i
变量分配值时,您只是对编译器说:“嘿,伙计,当您初始化该变量时,请将其值设置为...”。但是您不能使用它,直到它被初始化,因为它根本不存在。
更新:
如James Gosling,Bill Joy,Guy Steele和Gilad Bracha在《 Java语言规范》一书中所说:
这些限制旨在在编译时捕获循环或 否则格式错误的初始化。
考虑一下:
static {
i = 2;
j = i + 5; //should it be 7 or 15?
}
static int i = 10;
static int j;
变量j
是7还是15?
如果为7,则我们已经两次初始化i
变量,这是不可能的,因为该字段是静态的。如果是15岁,那么i = 2;
是什么意思?
此代码不明确,因此Java规范不允许这样做。
答案 1 :(得分:0)
进一步阅读后,我认为Pavel在这一点上不太准确,正如@Holger在评论中指出的那样。
我们已经将i变量初始化了两次,这是不可能的,因为该字段是静态的。
12.4.2. Detailed Initialization Procedure指出
对于每个类或接口C,都有一个唯一的初始化锁LC 。从C到LC的映射由Java虚拟机实现自行决定。
我想初始化两次对于类初始化器本身是可以的,只要对调用客户端一次就可以了。
但是Pavel提供的演示仍然保持原样,因此我基本上只在这里重用它,但有不同的解释。
static {
i = 2;
j = i + 5;
// no one knows whether "i" here initialized properly here
}
static int i = 10;
static int j;
但是,当您直接在MyClass.i
中使用j = MyClass.i + 5
时,编译器将知道这没问题,因为8.3.3. Forward References During Field Initialization有四个条件。
具体来说,如果以下所有全部是编译时错误 正确:
出现在类或接口C中的类变量的声明 在使用class变量后,以文字形式显示;
在C的任一类变量初始化器中使用的名称都是简单名称 或C的静态初始值设定项;
用法不在作业的左侧;
C是包含用途的最里面的类或接口。
此answer中已经有详细的讨论。
为了解决问题,我认为这是为了可预测的行为添加这些限制。再一次,8.3.3. Forward References During Field Initialization中指出了另一个官方目的。
这些限制旨在在编译时捕获循环或格式错误的初始化。
答案 2 :(得分:0)
同样适用于非静态成员。您可以在实例初始化程序块中为其分配值,但在初始化之前不能使用它们。
class Parent {
{
x = 3; // works fine
// System.out.println(x); // gives compilation error.
}
int x = 0;
public static void main(String[] args) {
}
}