反射、列挙と単例


通常私達が使っている一例のモデルは、私達はすべて反射を使ってもう一例ではなく、次のように餓漢式の一例のモードを使用できます.
public final class Singleton {

	private static final Singleton instance=new Singleton();
	
	private Singleton(){}
	
	public static Singleton getInstance(){
		return instance;
	}
}
テストの実例は以下の通りです.
Singleton singleton1=Singleton.getInstance();
		Singleton singleton2=Singleton.getInstance();
		
		Constructor<Singleton> constructor=Singleton.class.getDeclaredConstructor();
		constructor.setAccessible(true);
		Singleton singleton3=constructor.newInstance();
		
		System.out.println(singleton1);
		System.out.println(singleton2);
		System.out.println(singleton3);
		System.out.println(singleton1==singleton2);
		System.out.println(singleton1==singleton3);
この中のsingleton 1、singleton 2はいずれも私達が実現した単一の例のモードで獲得した対象であり、彼らは同一の対象であるべきです.singleton 3は無参構造器を反射して取得し、construct.setAccess ibleはアクセス権限を得て、最後に無参構造器を通じて一つのオブジェクトを作成します.テストの結果は以下の通りです.
com.lg.design.singleton.hungry.Singleton@15e3d24a
com.lg.design.singleton.hungry.Singleton@15e3d24a
com.lg.design.singleton.hungry.Singleton@20030380
true
false
だから、私たちが通常使っている一例のパターンは、反射を使ってもう一例ではないようにしてもいいです.しかし、一例で列挙すると、反射を避けることができます.単例は以下の通りです
public enum Singleton {
	
	instance;
	private Singleton(){}
	
}
反射は以下の通りです
Singleton singleton1=Singleton.instance;
		Singleton singleton2=Singleton.instance;
		
		Constructor<Singleton> constructor=Singleton.class.getDeclaredConstructor();
		constructor.setAccessible(true);
		Singleton singleton3=constructor.newInstance();
		
		System.out.println(singleton1);
		System.out.println(singleton2);
		System.out.println(singleton3);
		System.out.println(singleton1==singleton2);
		System.out.println(singleton1==singleton3);
エラーを報告します
Exception in thread "main" java.lang.NoSuchMethodException: com.lg.design.singleton.enumsingleton.Singleton.<init>()
	at java.lang.Class.getConstructor0(Class.java:2849)
	at java.lang.Class.getDeclaredConstructor(Class.java:2053)
	at com.lg.design.singleton.enumsingleton.Test.main(Test.java:14)
この無参コンストラクタがないです.Singleton.class.get Declard Controuctorstors()をデバッグしてすべてのコンストラクタを取得すると、私達が設置した無参画コンストラクタがないことが分かります.一つのパラメータだけが(String.class)、そしてここのパラメータは列挙の名前と場所です.列挙のnameとordinalの2つの属性で、列挙のソースコードは以下の通りです.
public abstract class Enum<E extends Enum<E>>
        implements Comparable<E>, Serializable {
   
    private final String name;

    public final String name() {
        return name;
    }

    private final int ordinal;

    public final int ordinal() {
        return ordinal;
    }

    protected Enum(String name, int ordinal) {
        this.name = name;
        this.ordinal = ordinal;
    }

    public String toString() {
        return name;
    }
 // 
}
エニュメレーエムは抽象的な類で、一つの類がエニュメレーションとして宣言したら、実はEnumを継承しています.だからこの文章を参考にできるということが分かりました.http://pf-miles.iteye.com/blog/187155#bc2340028. 父Enumのコンストラクタを入手できるなら、このコンストラクタを使ってオブジェクトを作成することができますか?
Singleton singleton1=Singleton.instance;
		Singleton singleton2=Singleton.instance;
		
		Constructor<Singleton> constructor=Singleton.class.getDeclaredConstructor(String.class,int.class);
		//Constructor<Singleton> constructor=Singleton.class.getDeclaredConstructor();
		constructor.setAccessible(true);
		Singleton singleton3=constructor.newInstance("otherInstance",9);
		//Singleton singleton3=constructor.newInstance();
		
		System.out.println(singleton1);
		System.out.println(singleton2);
		System.out.println(singleton3);
		System.out.println(singleton1==singleton2);
		System.out.println(singleton1==singleton3);
そしてエラーを報告します
Exception in thread "main" java.lang.IllegalArgumentException: Cannot reflectively create enum objects
	at java.lang.reflect.Constructor.newInstance(Constructor.java:521)
	at com.lg.design.singleton.enumsingleton.Test.main(Test.java:16)
前回のミスはコンストラクタがないということです.今回はコンストラクタを手に入れることができました.ただ、コンストラクタを使ってnewInstance(「other Instance」、9)を実行する時に異常を投げました.列挙を反射できないと言いました.具体的なソースは以下の通りです.
public T newInstance(Object ... initargs)
        throws InstantiationException, IllegalAccessException,
               IllegalArgumentException, InvocationTargetException
    {
        if (!override) {
            if (!Reflection.quickCheckMemberAccess(clazz, modifiers)) {
                Class<?> caller = Reflection.getCallerClass();
                checkAccess(caller, clazz, null, modifiers);
            }
        }
//       ,     ENUM  ,          
        if ((clazz.getModifiers() & Modifier.ENUM) != 0)
            throw new IllegalArgumentException("Cannot reflectively create enum objects");
        ConstructorAccessor ca = constructorAccessor;   // read volatile
        if (ca == null) {
            ca = acquireConstructorAccessor();
        }
        return (T) ca.newInstance(initargs);
    }
つまり、反射はnewInstanceでオブジェクトを作成する際に、このクラスがエニュメレート・クラスかどうかをチェックします.つまり、列挙を使うと反射を避けられ、一例の効果が得られます.転載する場合は出典を明記してください.   http://lgbolgger.iteye.com/blog/2159940 著者:iteyeの卓球狂