web-dev-qa-db-ja.com

シングルトンクラスを作成する方法

Javaでシングルトンクラスを作成する最良の/正しい方法は何ですか?

私が見つけた実装の1つは、プライベートコンストラクターとgetInstance()メソッドの使用です。

package singleton;

public class Singleton {

    private static Singleton me;

    private Singleton() {
    }

    public static Singleton getInstance() {
        if (me == null) {
            me = new Singleton();
        }

        return me;
    }
}

しかし、実装は次のテストケースで失敗します

package singleton;

import Java.lang.reflect.Constructor;
import Java.lang.reflect.InvocationTargetException;

public class Test {

    /**
     * @param args
     * @throws NoSuchMethodException
     * @throws SecurityException
     * @throws InvocationTargetException
     * @throws IllegalAccessException
     * @throws InstantiationException
     * @throws IllegalArgumentException
     */
    public static void main(String[] args) throws SecurityException,
            NoSuchMethodException, IllegalArgumentException,
            InstantiationException, IllegalAccessException,
            InvocationTargetException {
        Singleton singleton1 = Singleton.getInstance();
        System.out.println(singleton1);

        Singleton singleton2 = Singleton.getInstance();
        System.out.println(singleton2);

        Constructor<Singleton> c = Singleton.class
                .getDeclaredConstructor((Class<?>[]) null);
        c.setAccessible(true);
        System.out.println(c);

        Singleton singleton3 = c.newInstance((Object[]) null);
        System.out.println(singleton3);

        if(singleton1 == singleton2){
            System.out.println("Variable 1 and 2 referes same instance");
        }else{
            System.out.println("Variable 1 and 2 referes different instances");
        }
        if(singleton1 == singleton3){
            System.out.println("Variable 1 and 3 referes same instance");
        }else{
            System.out.println("Variable 1 and 3 referes different instances");
        }
    }

}

これを解決する方法は?

ありがとうございました

30
Arun P Johny

あなたの質問のコメントに従って:

アプリケーション全体で必要ないくつかのキーと値のペアを含むプロパティファイルがあるため、シングルトンクラスについて考えていました。このクラスはファイルからプロパティをロードして保持し、使用できますアプリケーション内のどこからでも

シングルトンを使用しないでください。一回限りlazy初期化は必要ないようです(これがシングルトンです)。ワンタイムdirect初期化が必要です。静的にして、静的初期化子にロードするだけです。

例えば。

public class Config {

    private static final Properties PROPERTIES = new Properties();

    static {
        try {
            PROPERTIES.load(Thread.currentThread().getContextClassLoader().getResourceAsStream("config.properties"));
        } catch (IOException e) {
            throw new ExceptionInInitializerError("Loading config file failed.", e);
        }
    }

    public static String getProperty(String key) {
        return PROPERTIES.getProperty(key);
    }

    // ...
}
18
BalusC

リフレクションを使用してカプセル化を貫通している場合、クラスの動作が誤った方法で変更されても驚かないでください。プライベートメンバーはクラスに対してプライベートであることになっています。リフレクションを使用してそれらにアクセスすることにより、意図的にクラスの動作を壊すことになり、結果として「重複したシングルトン」が期待されます。

要するに、それをしないでください。

また、静的コンストラクターでシングルトンインスタンスを作成することも検討できます。静的コンストラクターは同期され、1回だけ実行されます。現在のクラスには競合状態が含まれています-以前に呼び出されていないときに2つの別個のスレッドがgetInstance()を呼び出すと、2つのインスタンスが作成される可能性があります。 、およびその他は将来のgetInstance()呼び出しが返すインスタンスになります。

4
cdhowie

以下の方法でシングルトンを実装します。

から Singleton_pattern Initialization-on-demand holder idiom を使用してwikiepdiaによって記述されています

このソリューションは、特別な言語構造を必要とせずにスレッドセーフです(つまり、volatileまたはsynchronized

public final class  LazySingleton {
    private LazySingleton() {}
    public static LazySingleton getInstance() {
        return LazyHolder.INSTANCE;
    }
    private static class LazyHolder {
        private static final LazySingleton INSTANCE = new LazySingleton();
    }
    private Object readResolve()  {
        return LazyHolder.INSTANCE;
    }
}
3
Ravindra babu

Javaでシングルトンクラスを作成する最良の方法は、Enumを使用することです。

以下の例:

import Java.io.FileInputStream;
import Java.io.FileNotFoundException;
import Java.io.FileOutputStream;
import Java.io.IOException;
import Java.io.ObjectInputStream;
import Java.io.ObjectOutputStream;
import Java.io.Serializable;
import Java.lang.reflect.Constructor;
import Java.lang.reflect.InvocationTargetException;
import Java.lang.reflect.Method; 

enum SingleInstance{
    INSTANCE;

    private SingleInstance() {
        System.out.println("constructor");
    }   
}

public class EnumSingletonDemo {

    public static void main (String args[]) throws FileNotFoundException, IOException, ClassNotFoundException, NoSuchMethodException, SecurityException, InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException
    {
        SingleInstance s=SingleInstance.INSTANCE;
        SingleInstance s1=SingleInstance.INSTANCE;

        System.out.println(s.hashCode() + " "+s1.hashCode());//prints same hashcode indicates only one instance created

    //------- Serialization -------
    ObjectOutputStream oos=new ObjectOutputStream(new FileOutputStream("sample.ser"));
    oos.writeObject(s);
    oos.close();

    //------- De-Serialization -------
    ObjectInputStream ois=new ObjectInputStream(new FileInputStream("sample.ser"));
    SingleInstance s2=(SingleInstance) ois.readObject();

    System.out.println("Serialization :: "+s.hashCode()+" "+s2.hashCode());// prints same hashcodes because JVM handles serialization in case of enum(we dont need to override readResolve() method)

   //-----Accessing private enum constructor using Reflection-----

    Class c=Class.forName("SingleInstance");

    Constructor co=c.getDeclaredConstructor();//throws NoSuchMethodException
    co.setAccessible(true);
    SingleInstance newInst=(SingleInstance) co.newInstance();           

}
}

Reflectionを使用するプライベートコンストラクターを介して列挙型 'SingleInstance'の別のインスタンスを作成できないため、NoSuchMethodExceptionがスローされます。

シリアル化の場合、enumはデフォルトでシリアル化可能なインターフェースを実装します。

0