Ricerca…


introduzione

Un singleton è una classe che ha sempre una sola istanza. Per ulteriori informazioni sul modello di progettazione Singleton, fare riferimento all'argomento Singleton nel tag Design Patterns .

Enum Singleton

Java SE 5
public enum Singleton {
    INSTANCE;

    public void execute (String arg) {
        // Perform operation here 
    }
}

Le enumerazioni hanno costruttori privati, sono definitive e forniscono un adeguato meccanismo di serializzazione. Sono anche molto concisi e pigramente inizializzati in modo sicuro.

La JVM garantisce che i valori di enum non verranno istanziati più di una volta ciascuno, il che conferisce al modello singolare enum una difesa molto forte contro gli attacchi di riflessione.

Ciò di cui il modello enumerabile non protegge è rappresentato dagli altri sviluppatori che aggiungono fisicamente più elementi al codice sorgente. Di conseguenza, se scegli questo stile di implementazione per i tuoi singleton, è imperativo che tu dichiari chiaramente che non dovrebbero essere aggiunti nuovi valori a tali enumerazioni.

Questo è il modo consigliato di implementare il modello singleton, come spiegato da Joshua Bloch in Effective Java.

Filetto sicuro Singleton con doppio bloccaggio controllato

Questo tipo di Singleton è thread-safe e impedisce il blocco non necessario dopo la creazione dell'istanza Singleton.

Java SE 5
public class MySingleton {

    // instance of class
    private static volatile MySingleton instance = null;

    // Private constructor
    private MySingleton() {
        // Some code for constructing object
    }

    public static MySingleton getInstance() {
        MySingleton result = instance;
        
        //If the instance already exists, no locking is necessary
        if(result == null) {
            //The singleton instance doesn't exist, lock and check again
            synchronized(MySingleton.class) {
                result = instance;
                if(result == null) {
                    instance = result = new MySingleton();
                }
            }
        }
        return result;
    }
}

Va sottolineato - nelle versioni precedenti a Java SE 5, l'implementazione di cui sopra non è corretta e dovrebbe essere evitata. Non è possibile implementare il blocco del doppio controllo correttamente in Java prima di Java 5.

Singleton senza uso di Enum (inizializzazione desiderosa)

public class Singleton {    

    private static final Singleton INSTANCE = new Singleton();

    private Singleton() {}

    public static Singleton getInstance() {
        return INSTANCE;
    }
}

Si può sostenere che questo esempio è in effetti un'inizializzazione pigra. Sezione 12.4.1 degli stati della specifica del linguaggio Java :

Una classe o un tipo di interfaccia T verrà inizializzato immediatamente prima della prima occorrenza di una delle seguenti operazioni:

  • T è una classe e viene creata un'istanza di T
  • T è una classe e viene invocato un metodo statico dichiarato da T
  • Viene assegnato un campo statico dichiarato da T
  • Viene utilizzato un campo statico dichiarato da T e il campo non è una variabile costante
  • T è una classe di livello superiore e viene eseguita un'istruzione di asserzione nidificata in modo lessico in T.

Pertanto, fintanto che non ci sono altri campi statici o metodi statici nella classe, l'istanza Singleton non verrà inizializzata finché il metodo getInstance() viene invocato la prima volta.

Inizializzazione pigra sicura del thread usando la classe del titolare | Implementazione di Bill Pugh Singleton

public class Singleton {
    private static class InstanceHolder {
        static final Singleton INSTANCE = new Singleton();
    }

    public static Singleton getInstance() {
        return InstanceHolder.INSTANCE;
    }

    private Singleton() {}
}

Questo inizializza la variabile INSTANCE alla prima chiamata a Singleton.getInstance() , sfruttando le garanzie di sicurezza del thread del linguaggio per l'inizializzazione statica senza richiedere ulteriore sincronizzazione.

Questa implementazione è nota anche come modello di Bill Pugh singleton. [Wiki]

Estensione di singleton (ereditarietà singleton)

In questo esempio, la classe base Singleton fornisce il metodo getMessage() che restituisce "Hello world!" Messaggio.

Le sottoclassi UppercaseSingleton e LowercaseSingleton sostituiscono il metodo getMessage () per fornire una rappresentazione appropriata del messaggio.

//Yeah, we'll need reflection to pull this off.
import java.lang.reflect.*;

/*
Enumeration that represents possible classes of singleton instance.
If unknown, we'll go with base class - Singleton.
*/
enum SingletonKind {
    UNKNOWN,
    LOWERCASE,
    UPPERCASE
}

//Base class
class Singleton{

    /*
    Extended classes has to be private inner classes, to prevent extending them in 
    uncontrolled manner.
     */
    private class UppercaseSingleton extends Singleton {

        private UppercaseSingleton(){
            super();
        }

        @Override
        public String getMessage() {
            return super.getMessage().toUpperCase();
        }
    }

    //Another extended class.
    private class LowercaseSingleton extends Singleton
    {
        private LowercaseSingleton(){
            super();
        }

        @Override
        public String getMessage() {
            return super.getMessage().toLowerCase();
        }
    }

    //Applying Singleton pattern
    private static SingletonKind kind = SingletonKind.UNKNOWN;

    private static Singleton instance;

    /*
    By using this method prior to getInstance() method, you effectively change the
    type of singleton instance to be created.
     */
    public static void setKind(SingletonKind kind) {
        Singleton.kind = kind;
    }

    /*
    If needed, getInstance() creates instance appropriate class, based on value of
    singletonKind field.
     */
    public static Singleton getInstance() 
        throws  NoSuchMethodException, 
                IllegalAccessException, 
                InvocationTargetException, 
                InstantiationException {

        if(instance==null){
            synchronized (Singleton.class){
                if(instance==null){
                    Singleton singleton = new Singleton();
                    switch (kind){
                        case UNKNOWN:

                            instance = singleton;
                            break;

                        case LOWERCASE:

                            /*
                             I can't use simple

                             instance = new LowercaseSingleton();

                             because java compiler won't allow me to use
                             constructor of inner class in static context,
                             so I use reflection API instead.

                             To be able to access inner class by reflection API,
                             I have to create instance of outer class first.
                             Therefore, in this implementation, Singleton cannot be
                             abstract class.
                             */

                            //Get the constructor of inner class.
                            Constructor<LowercaseSingleton> lcConstructor =
                                    LowercaseSingleton.class.getDeclaredConstructor(Singleton.class);

                            //The constructor is private, so I have to make it accessible.
                            lcConstructor.setAccessible(true);

                            // Use the constructor to create instance.
                            instance = lcConstructor.newInstance(singleton);

                            break;

                        case UPPERCASE:

                            //Same goes here, just with different type
                            Constructor<UppercaseSingleton> ucConstructor =
                                    UppercaseSingleton.class.getDeclaredConstructor(Singleton.class);
                            ucConstructor.setAccessible(true);
                            instance = ucConstructor.newInstance(singleton);
                    }
                }
            }
        }
        return instance;
    }

    //Singletons state that is to be used by subclasses
    protected String message;

    //Private constructor prevents external instantiation.
    private Singleton()
    {
        message = "Hello world!";
    }

    //Singleton's API. Implementation can be overwritten by subclasses.
    public String getMessage() {
        return message;
    }
}

//Just a small test program
public class ExtendingSingletonExample {

    public static void main(String args[]){

        //just uncomment one of following lines to change singleton class

        //Singleton.setKind(SingletonKind.UPPERCASE);
        //Singleton.setKind(SingletonKind.LOWERCASE);

        Singleton singleton = null;
        try {
            singleton = Singleton.getInstance();
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        } catch (InstantiationException e) {
            e.printStackTrace();
        }
        System.out.println(singleton.getMessage());
    }
}


Modified text is an extract of the original Stack Overflow Documentation
Autorizzato sotto CC BY-SA 3.0
Non affiliato con Stack Overflow