柄にもなく(?)Javaネタです。。。
クラス変数にシングルトンをもたせる場合、コンストラクタ内で未初期化のクラス変数へのアクセスが発生する可能性があります。このあたりの確認です。
クラス変数としてシングルトンを生成・格納する場合
StaticNewInstance.java
package sandbox; public class StaticNewInstance { static { System.out.println("Start: Initialize static"); } private static StaticNewInstance instance = new StaticNewInstance(); private static Object o = new Object(); static { System.out.println("Done: Initialize static >> " + o); } private StaticNewInstance() { System.out.println("Start: Constructor"); System.out.println("Done: Constructor >> " + this.o); } public static void main(String[] args) { System.out.println("- MAIN"); } }
実行結果
Start: Initialize static Start: Constructor Done: Constructor >> null Done: Initialize static >> java.lang.Object@246bc73b - MAIN
クラスがロードされたタイミングで、クラス変数の初期化中にコンストラクタが呼ばれ、コンストラクタ内部で未初期化のクラス変数への参照が発生しています。
外部から普通にインスタンスを生成する場合
NewInstance.java
package sandbox; public class NewInstance { static { System.out.println("Start: Initialize static"); } public static Object o = new Object(); static { System.out.println("Done: Initialize static >> " + o); } public NewInstance() { System.out.println("Start: Constructor"); System.out.println("Done: Constructor >> " + this.o); } }
Test.java
package sandbox; public class Test { public static void main(String[] args) { System.out.println("- MAIN"); System.out.println("Start: Create instance"); NewInstance instance = new NewInstance(); System.out.println("Done: Create instance >> " + instance.o ); } }
実行結果
- MAIN Start: Create instance Start: Initialize static Done: Initialize static >> java.lang.Object@5270cdd2 Start: Constructor Done: Constructor >> java.lang.Object@5270cdd2 Done: Create instance >> java.lang.Object@5270cdd2
インスタンス生成に伴ってクラスがロードされると、クラス変数の初期化が終わった後にコンストラクタが呼び出されています。したがって、未初期化のクラス変数へのアクセスは発生しません。
Innerクラスのクラス変数にシングルトンを格納する場合
InnerStaticInstance.java
package sandbox; public class InnerStaticInstance { static { System.out.println("Start: Outer initialze static"); } private static class Inner { static { System.out.println("Start: Inner initialze static"); } private final static InnerStaticInstance outer = new InnerStaticInstance(); static { System.out.println("Done: Inner initialze static >> " + outer.o); } } static Object o = new Object(); static { System.out.println("Done: Outer initialze static"); } public static InnerStaticInstance getInstance() { System.out.println("Start: getInstance"); InnerStaticInstance singleton = Inner.outer; System.out.println("Done: getInstance >> " + singleton.o); return singleton; } private InnerStaticInstance() { System.out.println("Start: Constructor"); System.out.println("Done: Constructor >> " + this.o); } public static void main(String[] args) { System.out.println("- MAIN"); InnerStaticInstance.getInstance(); } }
実行結果
Start: Outer initialze static Done: Outer initialze static - MAIN Start: getInstance Start: Inner initialze static Start: Constructor Done: Constructor >> java.lang.Object@545eb748 Done: Inner initialze static >> java.lang.Object@545eb748 Done: getInstance >> java.lang.Object@545eb748
クラスロード時には、シングルトンの生成は行われずに、Outerクラスのクラス変数初期化が完了します。getInstance()のタイミングで、Innerクラスの初期化が行われて、Outerクラスのシングルトンの生成(Outerクラスのコンストラクタの実行)が行われます。つまり、Outerクラスのコンストラクタ実行時には、Outerクラスのクラス変数初期化は終わっており、未初期化のクラス変数へのアクセスは発生しません。
もちろん、次のように、Outerクラスのコンストラクタ内部で、Innerクラス内のクラス変数を参照すると未初期化の変数を参照させることは可能です。Innerクラスは、あくまでシングルトンの生成のためだけに使用する(その他のクラス変数などの余計な構造はもたせない)という前提になります。
InnerStaticInstance.java
package sandbox; public class InnerStaticInstance { static { System.out.println("Start: Outer initialze static"); } private static class Inner { static { System.out.println("Start: Inner initialze static"); } private final static InnerStaticInstance outer = new InnerStaticInstance(); static Object o = new Object(); static { System.out.println("Done: Inner initialze static >> " + outer.o); } } static Object o = new Object(); static { System.out.println("Done: Outer initialze static"); } public static InnerStaticInstance getInstance() { System.out.println("Start: getInstance"); InnerStaticInstance singleton = Inner.outer; System.out.println("Done: getInstance >> " + singleton.o); return singleton; } private InnerStaticInstance() { System.out.println("Start: Constructor"); System.out.println("Inner static >> " + Inner.o); System.out.println("Done: Constructor >> " + this.o); } public static void main(String[] args) { System.out.println("- MAIN"); InnerStaticInstance.getInstance(); } }
実行結果
Start: Outer initialze static Done: Outer initialze static - MAIN Start: getInstance Start: Inner initialze static Start: Constructor Inner static >> null Done: Constructor >> java.lang.Object@23f9e6e5 Done: Inner initialze static >> java.lang.Object@23f9e6e5 Done: getInstance >> java.lang.Object@23f9e6e5