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ę

Author: Arun P Johny, 2010-12-06

6 answers

Zgodnie z komentarzem do twojego pytania:

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

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.

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);
    }

    // ...
}
 17
Author: BalusC,
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ą.

 4
Author: cdhowie,
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;
    }
}
 3
Author: Ravindra babu,
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.

 0
Author: kalyani chaudhari,
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();
}
 -1
Author: Ram,
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(): SingletonClass

Kluczowy 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.
    }
}
 -2
Author: Chu Xiwen,
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