web-dev-qa-db-fra.com

Comment créer une classe singleton

Quel est le meilleur/correct moyen de créer une classe singleton en Java?

L'une des implémentations que j'ai trouvées utilise un constructeur privé et une méthode getInstance ().

package singleton;

public class Singleton {

    private static Singleton me;

    private Singleton() {
    }

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

        return me;
    }
}

Mais si la mise en œuvre échoue dans le cas de test suivant

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

}

Comment résoudre ce problème?

Je vous remercie

29
Arun P Johny

Selon le commentaire sur votre question: 

J'ai un fichier de propriétés contenant quelques paires de valeurs de clés, ce qui est nécessaire dans toute l'application, c'est pourquoi je pensais à une classe singleton. Cette classe chargera les propriétés d'un fichier et le conservera. Vous pourrez l'utiliser de n'importe où dans l'application

Ne pas utiliser un singleton. Vous n'avez apparemment pas besoin d'initialisation lazy une fois (c'est là que réside le singleton). Vous voulez une fois direct initialisation. Il suffit de le rendre statique et de le charger dans un initialiseur statique.

Par exemple.

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
BalusC

Si vous utilisez la réflexion pour percer l'encapsulation, vous ne devriez pas être surpris lorsque le comportement de votre classe est modifié de manière incorrecte. Les membres privés sont supposés être privés de la classe. En utilisant la réflexion pour y accéder, vous interrompez intentionnellement le comportement de la classe et vous obtenez le "singleton en double" résultant.

En bref: ne fais pas ça.

En outre, vous pouvez envisager de créer l’instance singleton dans un constructeur statique. Les constructeurs statiques sont synchronisés et ne s'exécutent qu'une fois. Votre classe actuelle contient une condition de concurrence critique: si deux threads distincts appellent getInstance() alors qu’elle n’a pas été appelée précédemment, il est possible que deux instances soient créées, l’une d’elles étant exclusive à l’un des threads et l’autre devenant le exemple, les futurs appels getInstance() seront renvoyés.

4
cdhowie

Je vais implémenter singleton de la manière suivante.

From Singleton_pattern décrit par wikiepdia en utilisant idiome titulaire initialisation sur demande

Cette solution est thread-safe sans nécessiter de construction de langage spéciale (c'est-à-dire volatile ou 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

Le meilleur moyen de créer Singleton Class en Java consiste à utiliser Enums.

Exemple comme ci-dessous: 

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 est levé car nous ne pouvons pas créer une autre instance de enum 'SingleInstance' via son constructeur privé à l'aide de Reflection.

En cas de sérialisation, enum implémente une interface sérialisable par défaut.

0
kalyani chaudhari