在Java中访问未初始化的静态字段

时间:2014-12-16 14:34:58

标签: java jvm javac static-members undefined-behavior

我非常感谢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

问题是:它肯定会打印0null,还是可以访问未初始化的内存并打印出其他内容?

即使这段代码通过编译器,为什么它不被JVM选中?

基于此,有没有办法做出讨厌的事情?

2 个答案:

答案 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"显示了比我能解释的更好的