Zoeken…


Invoering

Een singleton is een klasse die slechts één exemplaar heeft. Raadpleeg het onderwerp Singleton in de tag Design Patterns voor meer informatie over het ontwerppatroon Singleton .

Enum Singleton

Java SE 5
public enum Singleton {
    INSTANCE;

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

Enums hebben particuliere constructeurs, zijn definitief en bieden de juiste serialisatiemachines. Ze zijn ook erg beknopt en lui geïnitialiseerd op een thread-veilige manier.

De JVM biedt een garantie dat enum-waarden niet meer dan één keer per keer worden geïnstantieerd, wat het enum singleton-patroon een zeer sterke verdediging tegen reflectieaanvallen geeft.

Waar het opsommingspatroon niet tegen beschermt, zijn andere ontwikkelaars die fysiek meer elementen aan de broncode toevoegen. Als u deze implementatiestijl voor uw singletons kiest, is het daarom absoluut noodzakelijk dat u heel duidelijk documenteert dat er geen nieuwe waarden aan die enums moeten worden toegevoegd.

Dit is de aanbevolen manier om het singleton-patroon te implementeren, zoals uitgelegd door Joshua Bloch in Effective Java.

Rijgveilige singleton met dubbele gecontroleerde vergrendeling

Dit type Singleton is thread-safe en voorkomt onnodige vergrendeling nadat de Singleton-instantie is gemaakt.

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

Het moet worden benadrukt - in versies voorafgaand aan Java SE 5 is de bovenstaande implementatie onjuist en moet worden vermeden. Het is niet mogelijk om dubbel gecontroleerde vergrendeling correct in Java te implementeren vóór Java 5.

Singleton zonder Enum (enthousiaste initialisatie)

public class Singleton {    

    private static final Singleton INSTANCE = new Singleton();

    private Singleton() {}

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

Er kan worden betoogd dat dit voorbeeld effectief luie initialisatie is. Sectie 12.4.1 van de Java-taalspecificatie stelt:

Een klasse of interfacetype T wordt geïnitialiseerd onmiddellijk vóór het eerste optreden van een van de volgende:

  • T is een klasse en er wordt een instantie van T gemaakt
  • T is een klasse en een door T aangegeven statische methode wordt aangeroepen
  • Een statisch veld aangegeven door T wordt toegewezen
  • Een statisch veld aangegeven door T wordt gebruikt en het veld is geen constante variabele
  • T is een klasse op het hoogste niveau, en een assert-statement lexicaal genest in T wordt uitgevoerd.

Zolang er geen andere statische velden of statische methoden in de klasse zijn, wordt daarom de instantie Singleton niet geïnitialiseerd totdat de methode getInstance() de eerste keer wordt aangeroepen.

Draadveilige luie initialisatie met behulp van houderklasse | Bill Pugh Singleton implementatie

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

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

    private Singleton() {}
}

Hiermee wordt de INSTANCE variabele geïnitialiseerd bij de eerste aanroep van Singleton.getInstance() , waarbij gebruik wordt gemaakt van de veiligheidsgaranties van de taal voor statische initialisatie zonder aanvullende synchronisatie.

Deze implementatie is ook bekend als Bill Pugh singleton-patroon. [Wiki]

Singleton uitbreiden (singleton erfenis)

In dit voorbeeld biedt getMessage() Singleton methode getMessage() die "Hello world!" bericht.

Het subklassen UppercaseSingleton en LowercaseSingleton override GetMessage () methode om passende vertegenwoordiging van het signaal worden gegeven.

//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
Licentie onder CC BY-SA 3.0
Niet aangesloten bij Stack Overflow