Comment créer une classe singleton

Quelle est la meilleure façon / correcte 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 l'implémentation échoue-t-elle 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?

Merci

29
demandé sur Arun P Johny 2010-12-06 06:21:57

6 réponses

Selon le commentaire sur votre question:

j'ai un fichier de propriétés contenant des paires de valeurs de clés, ce qui est nécessaire dans l'application, c'est pourquoi je pensais à une classe singleton. Cette classe va charger les propriétés à partir d'un fichier et le conserver et vous pouvez l'utiliser depuis n'importe où dans l'application

N'utilisez pas de singleton. Vous n'avez apparemment pas besoin d'une initialisation unique lazy (c'est là qu'est un singleton). Vous voulez une seule fois initialisation directe . 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
répondu BalusC 2010-12-06 04:47:43

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 censés être privés à la classe. En utilisant la réflexion pour y accéder, vous cassez intentionnellement le comportement de la classe, et le "singleton en double" résultant est attendu.

En bref: ne faites pas ça.

En outre, vous pouvez envisager de créer l'instance singleton dans un constructeur statique. Statique les constructeurs sont synchronisés et ne s'exécuteront qu'une seule fois. Votre classe actuelle contient une condition de concurrence-si deux threads distincts appellent getInstance() alors qu'il n'a pas été précédemment appelé, il est possible que deux instances soient créées, l'une étant exclusive à l'un des threads, et l'autre devenant l'instance que les futurs appels getInstance() retourneront.

4
répondu cdhowie 2010-12-06 03:26:27

Je vais implémenter singleton de la manière ci-dessous.

De Singleton_pattern décrit par wikiepdia en utilisant idiome de support D'initialisation à la demande

Cette solution est thread-safe sans nécessiter de constructions de langage spéciales (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
répondu Ravindra babu 2016-06-19 16:01:40

La meilleure façon de créer une classe Singleton en java est d'utiliser des 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 lancé parce que nous ne pouvons pas créer une autre instance de enum 'SingleInstance' via son constructeur privé en utilisant la réflexion.

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

0
répondu kalyani chaudhari 2016-06-10 13:51:17

Je pense que vous pouvez vérifier si une instance existe déjà dans le constructeur et si existe lancer une exception

if(me != null){
    throw new InstanceAlreadyExistsException();
}
-1
répondu Ram 2010-12-06 03:24:00

Il suffit de suivre le diagramme de classe singleton pattern,

SingletonClass - singletonObject: SingletonClass - SingletonClass() + getObject (): SingletonClass

Point clé,

  • privé votre constructeur
  • l'instance de votre classe doit être dans la classe
  • fournissez la fonction pour renvoyer votre instance

Certains codes,

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
répondu Chu Xiwen 2014-01-09 23:39:02