Ash*_*hul 55 java final initialization
有些人可能会发现它类似于SO问题Java Final变量是否具有默认值?但是这个答案并没有完全解决这个问题,因为这个问题并没有直接在实例初始化程序块中打印x的值.
当我尝试在实例初始化程序块内直接打印x时,问题出现了,同时在块结束之前为x分配了一个值:
class HelloWorld {
final int x;
{
System.out.println(x);
x = 7;
System.out.println(x);
}
HelloWorld() {
System.out.println("hi");
}
public static void main(String[] args) {
HelloWorld t = new HelloWorld();
}
}
Run Code Online (Sandbox Code Playgroud)
这给出了编译时错误,指出变量x可能尚未初始化.
$ javac HelloWorld.java
HelloWorld.java:6: error: variable x might not have been initialized
System.out.println(x);
^
1 error
Run Code Online (Sandbox Code Playgroud)
我没有直接打印,而是调用一个打印功能:
class HelloWorld {
final int x;
{
printX();
x = 7;
printX();
}
HelloWorld() {
System.out.println("hi");
}
void printX() {
System.out.println(x);
}
public static void main(String[] args) {
HelloWorld t = new HelloWorld();
}
}
Run Code Online (Sandbox Code Playgroud)
这正确编译并提供输出
0
7
hi
Run Code Online (Sandbox Code Playgroud)
这两种情况之间的概念差异是什么?
Kon*_*kov 32
在JLS中,§8.3.3.前向引用在字段初始化期间,它声明在以下情况下存在编译时错误:
使用在使用后以声明方式显示声明的实例变量有时会受到限制,即使这些实例变量在范围内也是如此.具体来说,如果满足以下所有条件,则为编译时错误:
在使用实例变量之后,类或接口C中的实例变量的声明以文本形式出现;
在C的实例变量初始值设定项或C的实例初始值设定项中使用是一个简单的名称;
使用不在作业的左侧;
C是封闭使用的最内层类或接口.
以下规则附带一些示例,其中最接近您的示例是:
class Z {
static int peek() { return j; }
static int i = peek();
static int j = 1;
}
class Test {
public static void main(String[] args) {
System.out.println(Z.i);
}
}
Run Code Online (Sandbox Code Playgroud)
通过方法访问[到静态或实例变量] 不会以这种方式检查,因此上面的代码产生输出0,因为变量初始化器i使用类方法peek()访问变量的值j之前j已由其变量初始化程序初始化,at它仍然有其默认值(§4.12.5变量的初始值).
因此,总而言之,您的第二个示例编译并执行正常,因为编译器x在您调用printX()时不检查变量是否已经初始化,并且printX()在运行时实际发生x变量时,将为变量分配其默认值(0).
Tun*_*aki 12
阅读JLS,答案似乎在第16.2.2节:
空白
final成员字段V在块(第14.2节)之前是明确分配的(并且绝对不是未分配的),该块是在范围内V和在范围内声明的任何类的声明范围内的任何方法的主体V.
这意味着当调用方法时,在调用方法之前将最终字段分配给其默认值0,因此当您在方法内引用它时,它会成功编译并打印值0.
但是,当您访问方法之外的字段时,它被视为未分配,因此编译错误.以下代码也将无法编译:
public class Main {
final int x;
{
method();
System.out.println(x);
x = 7;
}
void method() { }
public static void main(String[] args) { }
}
Run Code Online (Sandbox Code Playgroud)
因为:
VS在块的任何其他语句之前是[un]赋值iffV在紧接S在块之前的语句之后被[un]赋值.
由于最终字段x在方法调用之前未分配,因此在它之后仍然未分配.
JLS中的这个注释也是相关的:
请注意,没有任何规则可以让我们得出结论,
V在块之前肯定是未分配的,该块是在其中声明的任何构造函数,方法,实例初始化程序或静态初始化程序的主体C.我们可以非正式地断定V在块之前没有明确地未分配,该块是在C中声明的任何构造函数,方法,实例初始化器或静态初始化器的主体,但是不需要明确声明这样的规则.