Java 防止循环初始化

对于某些对象实例而言, 变量就算是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);
    }
}