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");
}
}
}
これを解決する方法は?
ありがとうございました
あなたの質問のコメントに従って:
アプリケーション全体で必要ないくつかのキーと値のペアを含むプロパティファイルがあるため、シングルトンクラスについて考えていました。このクラスはファイルからプロパティをロードして保持し、使用できますアプリケーション内のどこからでも
シングルトンを使用しないでください。一回限り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);
}
// ...
}
リフレクションを使用してカプセル化を貫通している場合、クラスの動作が誤った方法で変更されても驚かないでください。プライベートメンバーはクラスに対してプライベートであることになっています。リフレクションを使用してそれらにアクセスすることにより、意図的にクラスの動作を壊すことになり、結果として「重複したシングルトン」が期待されます。
要するに、それをしないでください。
また、静的コンストラクターでシングルトンインスタンスを作成することも検討できます。静的コンストラクターは同期され、1回だけ実行されます。現在のクラスには競合状態が含まれています-以前に呼び出されていないときに2つの別個のスレッドがgetInstance()
を呼び出すと、2つのインスタンスが作成される可能性があります。 、およびその他は将来のgetInstance()
呼び出しが返すインスタンスになります。
以下の方法でシングルトンを実装します。
から 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;
}
}
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はデフォルトでシリアル化可能なインターフェースを実装します。