tecnicas - Pregunta de la entrevista: sobre la serialización de Java y singletons
entrevista tecnicas (6)
¿Es posible serializar un objeto singleton?
Depende de cómo se implemente el singleton. Si su singleton se implementa como un tipo de enumeración con un elemento, entonces es por defecto:
// Enum singleton - the preferred approach
public enum Elvis {
INSTANCE;
public void leaveTheBuilding() { ... }
}
Si su singleton no se implementa utilizando un tipo de elemento enum pero digamos usando un método de fábrica estático (la variante es usar un campo final público estático):
// Singleton with static factory
public class Elvis {
private static final Elvis INSTANCE = new Elvis();
private Elvis() { ... }
public static Elvis getInstance() { return INSTANCE; }
public void leaveTheBuilding() { ... }
}
Entonces no es suficiente agregar implements Serializable
para que sea serializable , debe declarar todos los campos de instancia como transitorios (para evitar un ataque de serialización) y proporcionar un método readResolve
.
Para mantener la garantía de singleton, debe declarar todos los campos de instancia como transitorios y proporcionar un método
readResolve
(Artículo 77). De lo contrario, cada vez que se deserializa una instancia serializada, se creará una nueva instancia, lo que llevará, en el caso de nuestro ejemplo, a falsos avistamientos de Elvis. Para evitar esto, agregue este métodoreadResolve
a la clase de Elvis:
// readResolve method to preserve singleton property private Object readResolve() { // Return the one true Elvis and let the garbage collector // take care of the Elvis impersonator. return INSTANCE; }
Esto se discute mucho en Java efectivo (que también muestra el ataque de serialización):
- Elemento 3: imponer la propiedad singleton con un constructor privado o un tipo enum
- Ítem 77: Por ejemplo control, prefiera los tipos enum para leer Resolver
en qué escenario deberíamos serializar un singleton
Por ejemplo, para el almacenamiento temporal a corto plazo o para transportar objetos a través de una red (con RMI, por ejemplo).
Y es posible diseñar una clase cuyo objeto no se puede serializar.
Como otros dijeron, no implemente Serializable
. E incluso si un objeto o una de sus superclases implementa Serializable
, aún puede evitar que se NotSerializableException
lanzando una NotSerializableException
desde writeObject()
.
En una entrevista, el entrevistador me hizo la siguiente pregunta: ¿es posible serializar un objeto singleton? Dije que sí, pero ¿en qué escenario deberíamos serializar un singleton?
¿Y es posible diseñar una clase cuyo objeto no se pueda serializar?
en ese escenario deberíamos serializar un singleton.
Imagine que tiene una aplicación de larga ejecución y desea poder cerrarla y luego continuar en el punto donde se apagó (por ejemplo, para realizar el mantenimiento del hardware). Si la aplicación utiliza un singleton con estado, debería poder guardar y restablecer el estado del sigleton, lo que se hace más fácilmente al serializarlo.
Y es posible diseñar una clase cuyo objeto no se puede serializar.
Muy simple: simplemente no implemente Serializable
y haga que la clase final
Dije si
No por defecto. Después de implementar java.io.Serializable
, debe reemplazar los readObject()
y writeObject()
readResolve()
porque no puede serializar los campos estáticos. Un singleton mantiene su instancia en un campo estático.
pero en qué escenario deberíamos serializar un singleton.
Ningún escenario útil del mundo real me viene a la mente en realidad. Un singleton por lo general no cambia de estado a lo largo de su vida útil ni contiene ningún estado que desee guardar / restaurar. Si lo hizo, entonces ya estaba mal convertirlo en singleton.
Dos ejemplos del mundo real del patrón singleton en Java SE API son java.lang.Runtime#getRuntime()
y java.awt.Desktop#getDesktop()
. Ninguno de ellos implementa serializable. Tampoco tiene ningún sentido porque a la derecha devuelve la instancia correcta / deseada / esperada en cada llamada. Si serializas y deserializas, puedes terminar con varias instancias. Si cambia de entorno mientras tanto, la instancia puede no funcionar en absoluto.
Y es posible diseñar una clase cuyo objeto no se pueda serializar.
Sí. Simplemente no permita que la clase implemente la interfaz java.io.Serializable
.
El problema con serializar un singleton es que terminas con dos, la original y la copia deserializada.
La forma más obvia de evitar la serialización es simplemente no implementar serializable. Sin embargo, a veces querrás que tu singleton implemente serializable para que pueda ser referenciado en un objeto serializado sin problemas.
Si eso es un problema, tiene algunas opciones. Lo mejor es hacer que el singleton sea una enumeración de miembro único, si es posible. De esta forma, la implementación Java subyacente se ocupa de todos los detalles.
Si eso no es posible, entonces debe implementar los métodos readObject y writeObject apropiados para garantizar que la serialización no se convierta en una copia por separado.
La pregunta probablemente debería redactarse mejor ya que "¿es posible utilizar serialización y deserialización con una clase C de patrón único de una manera que no rompa el patrón singleton?"
La respuesta es básicamente sí:
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectStreamException;
import java.io.Serializable;
public class AppState implements Serializable
{
private static AppState s_instance = null;
public static synchronized AppState getInstance() {
if (s_instance == null) {
s_instance = new AppState();
}
return s_instance;
}
private AppState() {
// initialize
}
private void readObject(ObjectInputStream ois) throws IOException, ClassNotFoundException {
ois.defaultReadObject();
synchronized (AppState.class) {
if (s_instance == null) {
// re-initialize if needed
s_instance = this; // only if everything succeeds
}
}
}
// this function must not be called other than by the deserialization runtime
private Object readResolve() throws ObjectStreamException {
assert(s_instance != null);
return s_instance;
}
public static void main(String[] args) throws Throwable {
assert(getInstance() == getInstance());
java.io.ByteArrayOutputStream baos = new java.io.ByteArrayOutputStream();
java.io.ObjectOutputStream oos = new java.io.ObjectOutputStream(baos);
oos.writeObject(getInstance());
oos.close();
java.io.InputStream is = new java.io.ByteArrayInputStream(baos.toByteArray());
ObjectInputStream ois = new ObjectInputStream(is);
AppState s = (AppState)ois.readObject();
assert(s == getInstance());
}
}
pero tenga en cuenta que es posible que existan varias instancias de AppState
con este código. Sin embargo, solo se hace referencia a uno. Los otros son elegibles para la recolección de basura, creada solo por el tiempo de ejecución de deserialización, por lo que no existen para fines prácticos.
Para respuestas a sus otras dos preguntas (¿En qué escenario debemos serializar un singleton? ¿Es posible diseñar una clase cuyo objeto no se puede serializar?), Vea la respuesta de @Michael Borgwardt .
Una clase que es serializable se puede instanciar deserializándola, lo que permite varias instancias, por lo que no es un singleton.
a su segunda pregunta, del documento de java para java.io.Serializable
La clase que implementa la interfaz java.io.Serializable habilita la serialización de una clase.
Entonces, para implementar una clase que no sea serializable, no implemente Serializable.