我非常感谢this answer这个想法。
以下代码编译,但肯定不应该编译。它在初始化之前使用x
的值。如果删除StaticAssign.
限定符,则不再编译。
public class StaticAssign {
public static final int x;
static {
System.out.println(StaticAssign.x);
x = 5;
}
public static void main(String[] args) {}
}
在我的机器上,这始终打印0
。如果我将x
的类型更改为String
,则会始终打印null
。
问题是:它肯定会打印0
或null
,还是可以访问未初始化的内存并打印出其他内容?
即使这段代码通过编译器,为什么它不被JVM选中?
基于此,有没有办法做出讨厌的事情?
答案 0 :(得分:2)
它实际上已经初始化了。全局范围中的变量会自动初始化。对象类型的变量将初始化为null原语,如int将初始化为0.必须初始化未在全局范围内声明的变量,即。在方法中声明。另一个问题是将其声明为final
这告诉编译器必须显式初始化它。因此,通过添加x=5
,您将绕过编译器错误,说它必须显式初始化。当您在运行时在此行之前访问它时,它将像全局范围中的任何其他原始int类型变量一样初始化为0.
答案 1 :(得分:0)
这是由于加载类的方式。
首先加载类StaticAssign
定义,并将字段初始化为默认值:
StaticAssign.x = 0;
然后执行初始化块。
System.out.println(StaticAssign.x);
x = 5;
有一个对StaticAssign.x
的引用,即当前的类。由于简单地忽略了递归初始化尝试,x
的值仍为0
。
这意味着:
System.out.println(StaticAssign.x)
有效,因为StaticAssign.x
是对 allready 加载类字段的引用。
但如果你做的话:
System.out.println(x)
然后x
是对最终未经初始化的字段的引用。
另一方面,您永远不会访问未初始化的内存。加载类定义时,在执行初始化块之前,将变量初始化为默认值。
编辑: 来自Java Puzzlers的一个很好的视频"Elvis Lives Again"显示了比我能解释的更好的