Sök…


Introduktion

En singleton är en klass som bara någonsin har en enda instans. För mer information om Singleton designmönster , se Singleton ämnet i taggen Design Patterns .

Enum Singleton

Java SE 5
public enum Singleton {
    INSTANCE;

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

Enums har privata konstruktörer, är slutgiltiga och tillhandahåller korrekt seriemaskiner. De är också mycket kortfattade och lata initialiserade på ett gängsäkert sätt.

JVM ger en garanti för att enumvärden inte kommer att instanseras mer än en gång var och en, vilket ger enum singletonmönstret ett mycket starkt försvar mot reflektionsattacker.

Vad enum-mönstret inte skyddar mot är att andra utvecklare fysiskt lägger till fler element i källkoden. Följaktligen, om du väljer den här implementeringsstilen för dina singletoner är det absolut nödvändigt att du mycket tydligt dokumenterar att inga nya värden ska läggas till i dessa enums.

Detta är det rekommenderade sättet att implementera singletonmönstret, som förklarats av Joshua Bloch i Effektiv Java.

Gängsäker Singleton med dubbelkontrollerad låsning

Denna typ av Singleton är trådsäker och förhindrar onödig låsning efter att Singleton-instansen har skapats.

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

Det måste betonas - i versioner före Java SE 5 är implementeringen ovan felaktig och bör undvikas. Det är inte möjligt att implementera dubbelkontrollerad låsning korrekt i Java före Java 5.

Singleton utan användning av Enum (ivrig initialisering)

public class Singleton {    

    private static final Singleton INSTANCE = new Singleton();

    private Singleton() {}

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

Det kan hävdas att detta exempel är en effektiv lat initiering. Avsnitt 12.4.1 i Java Language Specification anger:

En klass eller gränssnittstyp T kommer att initialiseras omedelbart innan den första förekomsten av något av följande:

  • T är en klass och en instans av T skapas
  • T är en klass och en statisk metod som deklarerats av T åberopas
  • Ett statiskt fält som deklarerats av T tilldelas
  • Ett statiskt fält som deklareras av T används och fältet är inte en konstant variabel
  • T är en klass på högsta nivå, och ett påstående uttalande som leksiskt är kapslad i T utförs.

Därför, så länge det inte finns några andra statiska fält eller statiska metoder i klassen, kommer Singleton instansen inte att initialiseras förrän metoden getInstance() åberopas första gången.

Tråd-säker lat initiering med hållarklass | Bill Pugh Singleton implementering

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

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

    private Singleton() {}
}

Detta initialiserar INSTANCE variabeln vid det första samtalet till Singleton.getInstance() , och utnyttjar språkets trådsäkerhetsgarantier för statisk initialisering utan att kräva ytterligare synkronisering.

Denna implementering är också känd som Bill Pugh singletonmönster. [Wiki]

Utöka singleton (singletonarv)

I det här exemplet tillhandahåller getMessage() Singleton getMessage() -metoden som returnerar "Hello world!" meddelande.

Det är underklasser UppercaseSingleton och LowercaseSingleton åsidosätter metoden getMessage () för att ge lämplig representation av meddelandet.

//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
Licensierat under CC BY-SA 3.0
Inte anslutet till Stack Overflow