Prerrequisito: Patrón Singleton
En este artículo, veremos cuáles son los diversos conceptos que pueden romper la propiedad singleton de una clase y cómo evitarlos. Hay principalmente 3 conceptos que pueden romper la propiedad singleton de una clase. Discutámoslos uno por uno.
- Reflexión : se puede hacer que la reflexión destruya la propiedad singleton de la clase singleton, como se muestra en el siguiente ejemplo:
Java
// Java code to explain effect of Reflection
// on Singleton property
import
java.lang.reflect.Constructor;
// Singleton class
class
Singleton
{
// public instance initialized when loading the class
public
static
Singleton instance =
new
Singleton();
private
Singleton()
{
// private constructor
}
}
public
class
GFG
{
public
static
void
main(String[] args)
{
Singleton instance1 = Singleton.instance;
Singleton instance2 =
null
;
try
{
Constructor[] constructors =
Singleton.
class
.getDeclaredConstructors();
for
(Constructor constructor : constructors)
{
// Below code will destroy the singleton pattern
constructor.setAccessible(
true
);
instance2 = (Singleton) constructor.newInstance();
break
;
}
}
catch
(Exception e)
{
e.printStackTrace();
}
System.out.println(
"instance1.hashCode():- "
+ instance1.hashCode());
System.out.println(
"instance2.hashCode():- "
+ instance2.hashCode());
}
}
Output:- instance1.hashCode():- 366712642 instance2.hashCode():- 1829164700
Después de ejecutar esta clase, verá que los hashCodes son diferentes, lo que significa que se crean 2 objetos de la misma clase y se destruye el patrón singleton.
Superar el problema de reflexión: para superar el problema planteado por la reflexión, se utilizan enumeraciones porque Java garantiza internamente que el valor de enumeración se instancia solo una vez. Dado que Java Enums es accesible globalmente, se pueden usar para singletons. Su único inconveniente es que no es flexible, es decir, no permite la inicialización diferida.
Java
//Java program for Enum type singleton
public
enum
Singleton
{
INSTANCE;
}
Como las enumeraciones no tienen ningún constructor, no es posible que Reflection las utilice. Las enumeraciones tienen su constructor predeterminado, no podemos invocarlas nosotros mismos. JVM maneja la creación e invocación de constructores de enumeración internamente. Como las enumeraciones no dan su definición de constructor al programa, no es posible que accedamos a ellas mediante Reflection también. Por lo tanto, la reflexión no puede romper la propiedad singleton en caso de enumeraciones.
- Serialización: la serialización también puede provocar la rotura de la propiedad singleton de las clases singleton. La serialización se utiliza para convertir un objeto de flujo de bytes y guardarlo en un archivo o enviarlo a través de una red. Suponga que serializa un objeto de una clase singleton. Luego, si deserializa ese objeto, creará una nueva instancia y, por lo tanto, romperá el patrón de singleton.
Java
// Java code to explain effect of
// Serialization on singleton classes
import
java.io.FileInputStream;
import
java.io.FileOutputStream;
import
java.io.ObjectInput;
import
java.io.ObjectInputStream;
import
java.io.ObjectOutput;
import
java.io.ObjectOutputStream;
import
java.io.Serializable;
class
Singleton
implements
Serializable
{
// public instance initialized when loading the class
public
static
Singleton instance =
new
Singleton();
private
Singleton()
{
// private constructor
}
}
public
class
GFG
{
public
static
void
main(String[] args)
{
try
{
Singleton instance1 = Singleton.instance;
ObjectOutput out
=
new
ObjectOutputStream(
new
FileOutputStream(
"file.text"
));
out.writeObject(instance1);
out.close();
// deserialize from file to object
ObjectInput in
=
new
ObjectInputStream(
new
FileInputStream(
"file.text"
));
Singleton instance2 = (Singleton) in.readObject();
in.close();
System.out.println(
"instance1 hashCode:- "
+ instance1.hashCode());
System.out.println(
"instance2 hashCode:- "
+ instance2.hashCode());
}
catch
(Exception e)
{
e.printStackTrace();
}
}
}
Output:- instance1 hashCode:- 1550089733 instance2 hashCode:- 865113938
Como puede ver, el código hash de ambas instancias es diferente, por lo tanto, hay 2 objetos de una clase única. Por lo tanto, la clase ya no es singleton.
Superar el problema de serialización: – Para superar este problema, tenemos que implementar el método readResolve().
Java
// Java code to remove the effect of
// Serialization on singleton classes
import
java.io.FileInputStream;
import
java.io.FileOutputStream;
import
java.io.ObjectInput;
import
java.io.ObjectInputStream;
import
java.io.ObjectOutput;
import
java.io.ObjectOutputStream;
import
java.io.Serializable;
class
Singleton
implements
Serializable
{
// public instance initialized when loading the class
public
static
Singleton instance =
new
Singleton();
private
Singleton()
{
// private constructor
}
// implement readResolve method
protected
Object readResolve()
{
return
instance;
}
}
public
class
GFG
{
public
static
void
main(String[] args)
{
try
{
Singleton instance1 = Singleton.instance;
ObjectOutput out
=
new
ObjectOutputStream(
new
FileOutputStream(
"file.text"
));
out.writeObject(instance1);
out.close();
// deserialize from file to object
ObjectInput in
=
new
ObjectInputStream(
new
FileInputStream(
"file.text"
));
Singleton instance2 = (Singleton) in.readObject();
in.close();
System.out.println(
"instance1 hashCode:- "
+ instance1.hashCode());
System.out.println(
"instance2 hashCode:- "
+ instance2.hashCode());
}
catch
(Exception e)
{
e.printStackTrace();
}
}
}
Output:- instance1 hashCode:- 1550089733 instance2 hashCode:- 1550089733
Por encima de ambos códigos hash son iguales, por lo que no se crea ninguna otra instancia.
- Clonación: La clonación es un concepto para crear objetos duplicados. Usando clon podemos crear una copia del objeto. Supongamos que creamos un clon de un objeto singleton, luego creará una copia en la que hay dos instancias de una clase singleton, por lo tanto, la clase ya no es singleton.
Java
// Java code to explain cloning
// issue with singleton
class
SuperClass
implements
Cloneable
{
int
i =
10
;
@Override
protected
Object clone()
throws
CloneNotSupportedException
{
return
super
.clone();
}
}
// Singleton class
class
Singleton
extends
SuperClass
{
// public instance initialized when loading the class
public
static
Singleton instance =
new
Singleton();
private
Singleton()
{
// private constructor
}
}
public
class
GFG
{
public
static
void
main(String[] args)
throws
CloneNotSupportedException
{
Singleton instance1 = Singleton.instance;
Singleton instance2 = (Singleton) instance1.clone();
System.out.println(
"instance1 hashCode:- "
+ instance1.hashCode());
System.out.println(
"instance2 hashCode:- "
+ instance2.hashCode());
}
}
Output :- instance1 hashCode:- 366712642 instance2 hashCode:- 1829164700
Dos hashCode diferentes significan que hay 2 objetos diferentes de clase singleton.
Superar el problema de clonación: – Para superar este problema, anule el método clone() y lance una excepción del método de clonación que sea CloneNotSupportedException. Ahora, cada vez que el usuario intente crear un clon del objeto singleton, generará una excepción y, por lo tanto, nuestra clase seguirá siendo singleton.
Java
// Java code to explain overcome
// cloning issue with singleton
class
SuperClass
implements
Cloneable
{
int
i =
10
;
@Override
protected
Object clone()
throws
CloneNotSupportedException
{
return
super
.clone();
}
}
// Singleton class
class
Singleton
extends
SuperClass
{
// public instance initialized when loading the class
public
static
Singleton instance =
new
Singleton();
private
Singleton()
{
// private constructor
}
@Override
protected
Object clone()
throws
CloneNotSupportedException
{
throw
new
CloneNotSupportedException();
}
}
public
class
GFG
{
public
static
void
main(String[] args)
throws
CloneNotSupportedException
{
Singleton instance1 = Singleton.instance;
Singleton instance2 = (Singleton) instance1.clone();
System.out.println(
"instance1 hashCode:- "
+ instance1.hashCode());
System.out.println(
"instance2 hashCode:- "
+ instance2.hashCode());
}
}
Output:- Exception in thread "main" java.lang.CloneNotSupportedException at GFG.Singleton.clone(GFG.java:29) at GFG.GFG.main(GFG.java:38)
Ahora hemos detenido al usuario para crear un clon de la clase singleton. Si no desea lanzar una excepción, también puede devolver la misma instancia desde el método de clonación.
Java
// Java code to explain overcome
// cloning issue with singleton
class
SuperClass
implements
Cloneable
{
int
i =
10
;
@Override
protected
Object clone()
throws
CloneNotSupportedException
{
return
super
.clone();
}
}
// Singleton class
class
Singleton
extends
SuperClass
{
// public instance initialized when loading the class
public
static
Singleton instance =
new
Singleton();
private
Singleton()
{
// private constructor
}
@Override
protected
Object clone()
throws
CloneNotSupportedException
{
return
instance;
}
}
public
class
GFG
{
public
static
void
main(String[] args)
throws
CloneNotSupportedException
{
Singleton instance1 = Singleton.instance;
Singleton instance2 = (Singleton) instance1.clone();
System.out.println(
"instance1 hashCode:- "
+ instance1.hashCode());
System.out.println(
"instance2 hashCode:- "
+ instance2.hashCode());
}
}
Output:- instance1 hashCode:- 366712642 instance2 hashCode:- 366712642
Ahora, como el código hash de ambas instancias es el mismo, significa que representan una sola instancia.
Este artículo es una contribución de Vishal Garg . Si te gusta GeeksforGeeks y te gustaría contribuir, también puedes escribir un artículo usando write.geeksforgeeks.org o enviar tu artículo por correo a review-team@geeksforgeeks.org. Vea su artículo que aparece en la página principal de GeeksforGeeks y ayude a otros Geeks.
Escriba comentarios si encuentra algo incorrecto o si desea compartir más información sobre el tema tratado anteriormente.
Publicación traducida automáticamente
Artículo escrito por GeeksforGeeks-1 y traducido por Barcelona Geeks. The original can be accessed here. Licence: CCBY-SA