技術開発日記

技術やら日々思ったことを綴ってます。

シングルトンの変数

シングルトンの生成の方法はよく議論されるところなんだけど、
それとは別にシングルトンクラスの変数の初期化に関して少し予想外の挙動が起きたので、
メモしておきたいと思う。

public class Sample {

    public static void main(String args[]) {
        Single.getInstance();
    }    
}

class Single {
    
    private static final Single single = new Single();
    
    // 定数
    private static final Integer NUM1 = new Integer("1");
    private static final int NUM2 = 2;
    
    // コンストラクラタ
    private Single() {
        System.out.println(NUM1);
        System.out.println(NUM2);
    }
    
    public static Single getInstance() {
        return single;
    }
}

結果:
null
2

てっきり「1」「2」って出力されると思っていたから、これはちょっと予想外だった。
でもなんでオブジェクト型の方だけ初期化されないのかが、さっぱりわからない。
定数って型に関係なくクラスがロードされた時点(getInstace()の時点)で初期化されると思っていたから、これはもしかして、メモリ管理の違いかなとも思ったりもした。
オブジェクト型はヒープ領域だし、プリミティブ型はスタック領域。
ここの違いがもしかしたら原因かもしれないけど、それもなかなか情報がなかったせいかこれもまったくわからない。

ただ、定数の宣言場所を変えるとなぜか正常に値が取れる。

class Single {
    
    // 定数(インスタンスの生成の前に変更)
    private static final Integer NUM1 = new Integer("1");
    private static final int NUM2 = 2;
    
    private static final Single single = new Single();
    
    // コンストラクラタ
    private Single() {
        System.out.println(NUM1);
        System.out.println(NUM2);
    }
    
    public static Single getInstance() {
        return single;
    }
}

結果:
1
2

他にも変数のstaticを外すと上記と同様に問題なく値が取得できる。

class Single {
    
    private static Single single = new Single();
    
    // staticを外した変数
    private final Integer num1 = new Integer("1");
    private final int num2 = 2;
    
    // コンストラクラタ
    private Single() {
        System.out.println(num1);
        System.out.println(num2);
    }
    
    public static Single getInstance() {
        return single;
    }
}

結果:
1
2

このことからもstatic修飾子が関係しているのが明らかなんだけど、肝心の「なんでそうなるか」は結局わからなかった。。
わかる人がいればぜひ教えていただきた限りです。

ただ、よくよく考えてみるとコンストラクラタで定数を初期化するのも微妙な上に、普通シングルトンのコンストラクラタで変数をどうこうするってこともあまり無い気がするから、ひとまずこういう現象が起こりえるっていうことだけ注意しておけばいいかな。