Java Language
Singletons
Buscar..
Introducción
Un singleton es una clase que solo tiene una sola instancia. Para obtener más información sobre el patrón de diseño Singleton, consulte el tema Singleton en la etiqueta Patrones de diseño .
Enum Singleton
public enum Singleton {
INSTANCE;
public void execute (String arg) {
// Perform operation here
}
}
Las enumeraciones tienen constructores privados, son definitivas y proporcionan la maquinaria de serialización adecuada. También son muy concisos y están perezosamente iniciados de manera segura para los hilos.
La JVM ofrece una garantía de que los valores de enumeración no se instanciarán más de una vez, lo que otorga al patrón de enumeración de enumeración una defensa muy fuerte contra los ataques de reflexión.
Lo que no protege contra el patrón de enumeración es que otros desarrolladores agreguen físicamente más elementos al código fuente. Por consiguiente, si elige este estilo de implementación para sus singletons, es imperativo que documente claramente que no se deben agregar nuevos valores a esas enumeraciones.
Esta es la forma recomendada de implementar el patrón de singleton, como lo explica Joshua Bloch en Effective Java.
Hilo seguro Singleton con doble control de bloqueo
Este tipo de Singleton es seguro para subprocesos y evita el bloqueo innecesario después de que se haya creado la instancia de Singleton.
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;
}
}
Se debe enfatizar: en las versiones anteriores a Java SE 5, la implementación anterior es incorrecta y debe evitarse. No es posible implementar el bloqueo de doble comprobación correctamente en Java antes de Java 5.
Singleton sin uso de Enum (inicialización impaciente)
public class Singleton {
private static final Singleton INSTANCE = new Singleton();
private Singleton() {}
public static Singleton getInstance() {
return INSTANCE;
}
}
Se puede argumentar que este ejemplo es efectivamente una inicialización perezosa. La sección 12.4.1 de la especificación del lenguaje Java establece:
Una clase o tipo de interfaz T se inicializará inmediatamente antes de la primera aparición de cualquiera de los siguientes:
- T es una clase y se crea una instancia de T
- T es una clase y se invoca un método estático declarado por T
- Se asigna un campo estático declarado por T
- Se usa un campo estático declarado por T y el campo no es una variable constante
- T es una clase de nivel superior y se ejecuta una declaración de afirmación anidada léxicamente dentro de T.
Por lo tanto, mientras no haya otros campos estáticos o métodos estáticos en la clase, la instancia de Singleton
no se inicializará hasta que se invoque el método getInstance()
primera vez.
Inicialización perezosa segura para subprocesos utilizando la clase de soporte | Implementación de 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() {}
}
Esto inicializa la variable INSTANCE
en la primera llamada a Singleton.getInstance()
, aprovechando las garantías de seguridad de subprocesos del idioma para la inicialización estática sin necesidad de sincronización adicional.
Esta implementación también se conoce como patrón singleton de Bill Pugh. [Wiki]
Extendiendo singleton (herencia singleton)
En este ejemplo, la clase base Singleton
proporciona el método getMessage()
que devuelve "Hello world!"
mensaje.
Es subclases UppercaseSingleton
y LowercaseSingleton
reemplazar el método getMessage () para proporcionar una representación apropiada del mensaje.
//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());
}
}