对于某些对象实例而言, 变量就算是static final的, 它们的初始化也可能会被安排在后期进行
声明一个字段为static final并不能保证它在被读之前已经完全初始化
仔细理解这两句话,然后来看下面这段代码(keng)
public class Cycle {
private final int result;
private static final Cycle self = new Cycle();
private static final int randomNumber = (int)(Math.random()*100);
public Cycle(){
System.out.println("randomNumber:"+randomNumber);
result = randomNumber - 10;
}
public static void main(String args[]){
System.out.println(self.result);
}
}
上面这段代码会永远输出 -10 , 而不是随机值

再仔细看看这句话
当你使用一个类的静态成员变量时,会触发这个类的构造器函数 |
也就是说当代码执行到第四行
private static final Cycle self = new Cycle();
此时出现了第一个静态变量self, 接着jvm就需要调用构造器了
public Cycle(){
System.out.println("randomNumber:"+randomNumber);
result = randomNumber - 10;
}
注意此时randomNumber的值还是0 , 因为它还没有被赋值
正确的做法是在类调用构造器之前初始化randomNumber
public class CycleCorrected {
private static final int randomNumber = (int)(Math.random()*100);
private final int result;
private static final CycleCorrected self = new CycleCorrected();
public CycleCorrected(){
System.out.println("randomNumber:"+randomNumber);
result = randomNumber - 10;
}
public static void main(String args[]){
System.out.println(self.result);
}
}

其实上面的代码稍作改动, 还可以观察到一个Exception in thread “main” java.lang.StackOverflowError
当然前提是我的内存堆够大,所以出现了Stack溢出而不是堆内存溢出
1. 把self变量改成非静态的, 这样main函数需要new一个实例
2. 当你new出一个App的实例时, 由于它包含一个self, 所以它需要调用new App()来初始化这个实例, 但是被new出来的实例又包含了一个self…
其实是一个new方法的构造函数递归
3. 为什么之前没有出现溢出呢? 因为static变量的self, 只会被初始化一次
public class App {
private final int result;
private final App self = new App();
private static final int randomNumber = (int)(Math.random()*100);
public App(){
System.out.println("randomNumber:"+randomNumber);
result = randomNumber - 10;
}
public static void main(String args[]){
System.out.println(new App().result);
}
}