Jak utworzyć klasę singleton
Jaki jest najlepszy / poprawny sposób tworzenia klasy singleton w Javie?
Jedną z implementacji, którą znalazłem, jest użycie prywatnego konstruktora i metody getInstance ().
package singleton;
public class Singleton {
private static Singleton me;
private Singleton() {
}
public static Singleton getInstance() {
if (me == null) {
me = new Singleton();
}
return me;
}
}
Ale czy implementacja nie powiedzie się w następującym przypadku testowym
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");
}
}
}
Jak to rozwiązać?
Dziękuję
6 answers
Zgodnie z komentarzem do twojego pytania:
Nie używaj Singletona. Najwyraźniej nie potrzebujesz jednorazowej leniwej inicjalizacji (tam chodzi o singleton). Chcesz jednorazowo bezpośrednia inicjalizacja. Po prostu zrób to statyczne i załaduj go do statycznego inicjalizatora.mam Plik Właściwości zawierający kilka par wartości kluczy, które są potrzebne w całej aplikacji, dlatego myślałem o klasie singleton. Ta klasa załaduje właściwości z pliku i zachowa je i można z nich korzystać z dowolnego miejsca w aplikacji
Np.
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);
}
// ...
}
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-12-06 04:47:43
Jeśli używasz reflection do przebijania enkapsulacji, nie powinieneś być zaskoczony, gdy zachowanie twojej klasy jest zmieniane w niewłaściwy sposób. Członkowie prywatni powinni być prywatni dla klasy. Używając reflection, aby uzyskać do nich dostęp, celowo łamiesz zachowanie klasy, A wynikający z tego "duplicate singleton" jest oczekiwany.
W skrócie: nie rób tego.Możesz również rozważyć utworzenie instancji singleton w statycznym konstruktorze. Static konstruktory są zsynchronizowane i będą działać tylko raz. Twoja obecna klasa zawiera warunek race - jeśli dwa oddzielne wątki wywołają getInstance()
, gdy nie zostały wcześniej wywołane, istnieje możliwość, że dwie instancje zostaną utworzone, jedna z nich będzie wyłączna dla jednego z wątków, a druga stanie się instancją, którą przyszłe wywołania getInstance()
powrócą.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-12-06 03:26:27
Zaimplementuję singleton w poniższy sposób.
From Singleton_pattern opisany przez wikiepdię za pomocą Inicjalizacja-idiom posiadacza na żądanie
To rozwiązanie jest bezpieczne dla wątków bez konieczności stosowania specjalnych konstrukcji językowych (np. volatile
lub 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;
}
}
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-06-19 16:01:40
Najlepszym sposobem na stworzenie klasy Singleton w Javie jest użycie Enums.
Przykład jak poniżej:
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();
}
}
NoSuchMethodException jest wyrzucany, ponieważ nie możemy utworzyć innej instancji Enum 'SingleInstance' poprzez jej prywatny konstruktor używając Reflection.
W przypadku serializacji enum domyślnie implementuje interfejs serializowalny.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-06-10 13:51:17
Myślę, że możesz sprawdzić, czy instancja już istnieje w konstruktorze i jeśli istnieje, rzuć wyjątek
if(me != null){
throw new InstanceAlreadyExistsException();
}
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-12-06 03:24:00
Po prostu postępuj zgodnie ze schematem klasy singleton pattern,
SingletonClass - singletonObject: SingletonClass - SingletonClass() + getObject(): SingletonClassKluczowy punkt,
- prywatny konstruktor
- instancja twojej klasy powinna znajdować się wewnątrz klasy
- podaj funkcję zwracającą Twoją instancję
Jakiś kod,
public class SingletonClass {
private static boolean hasObject = false;
private static SingletonClass singletonObject = null;
public static SingletonClass getObject() {
if (hasObject) {
return singletonObject;
} else {
hasObject = true;
singletonObject = new SingletonClass();
return singletonObject;
}
}
private SingletonClass() {
// Initialize your object.
}
}
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-01-09 23:39:02