2009-08-29 10 views
22

Estoy utilizando la interfaz Java Serializable y ObjectOutputStream para serializar objetos (hasta ahora, este método ha sido suficiente para mis propósitos).¿La serialización preserva la identidad del objeto?

Mi API se basa en la identidad del objeto para algunas operaciones y me pregunto si se conservará por serialización. Es decir: si, para dos objetos arbitrarios a y b, contiene a == b antes de la serialización, ¿se retiene después de la deserialización?

He encontrado algunos textos que claim the contrary - pero o bien escribieron sobre una versión anterior de JRE (estoy interesado solo en 1.6 y quizás 1.5), o estaban preocupados con RMI (que no es relevante para mí)

La documentation no es muy próxima a la identidad del objeto. Un technical article en sun.com menciona que ObjectOutputStream usa el almacenamiento en memoria caché en objetos, lo cual para mí solo tiene sentido si la identidad del objeto está efectivamente preservada, pero no tengo la confianza suficiente para confiar en esta evidencia endeble.

Lo he intentado salir (Java 1.6, OS X) y encontraron que , la identidad de los objetos se mantiene sin cambios por la serialización. ¿Pero puedo extrapolar de estos resultados o no son confiables?

Para mi prueba, he serializa el gráfico siguiente objeto:

C----------+ 
| b1 b2 | 
+----------+ 
    |  | 
    v  v 
B---+ B---+ 
| a | | a | 
+---+ +---+ 
    \ /
    \/
    \/ 
    A----+ 
    | | 
    +----+ 

Un código reproducción mínima:

import java.io.*; 

public class SerializeTest { 
    static class A implements Serializable {} 

    static class B implements Serializable { 
     final A a; 

     public B(A a) { 
      this.a = a; 
     } 
    } 

    static class C implements Serializable { 
     final B b1, b2; 

     public C() { 
      A object = new A(); 
      b1 = b2 = new B(object); 
     } 
    } 

    public static void main(String[] args) throws IOException, 
      ClassNotFoundException { 
     C before = new C(); 
     System.out.print("Before: "); 
     System.out.println(before.b1.a == before.b2.a); 

     // Serialization. 
     ByteArrayOutputStream data = new ByteArrayOutputStream(); 
     ObjectOutputStream out = new ObjectOutputStream(data); 
     out.writeObject(before); 
     out.close(); 

     // Deserialization. 
     ObjectInputStream in = 
      new ObjectInputStream(new ByteArrayInputStream(data.toByteArray())); 
     C after = (C) in.readObject(); 
     System.out.print("After: "); 
     System.out.println(after.b1.a == after.b2.a); 
    } 
} 
+0

Si mantiene que a == b entonces no son objetos arbitrarios. Creo que tu pregunta va por las referencias. –

Respuesta

18

Durante dos objetos arbitrarios A y B, si se mantiene a == b antes de la serialización, todavía mantener cierto después de deserialización SI:

  1. Tanto a como b se escriben como y posteriormente leen de como partes de la misma corriente.Aquí hay una cita de la documentación de ObjectInputStream: "Los gráficos de los objetos se restauran correctamente utilizando un mecanismo de referencia compartida que comparte".
  2. La clase de ayb no anula readResolve() que tiene el potencial de cambiar la forma en que se restauran las referencias; tampoco las clases que tienen a y b.

Para el resto de los casos, NO se preservará la identidad del objeto.

+0

ponerme mejor que yo creo –

+2

Puede que quiera agregar las consecuencias de usar el método writeUnshared() de la clase ObjectOutputStream a su respuesta. Termina creando nuevos objetos únicos en la secuencia. Hay más información en la Especificación de Serialización de Objetos de Java en http://java.sun.com/j2se/1.4/pdf/serial-spec.pdf –

+0

Gracias, eso es exactamente lo que he estado buscando. En mi caso, tengo suerte. La documentación de 'ObjectInputStream' podría ser más explícita aquí, sin embargo. Sin definición, "compartir información" es un término bastante opaco. @Vineet: gracias por vincular las especificaciones. –

10

La respuesta es sin, por la identidad de objeto por defecto no se conserva vía serialización si está considerando 2 serializaciones separadas de un objeto/gráfico dado. Por ejemplo, si un serializo un objeto por el cable (quizás lo envío de un cliente a un servidor a través de RMI), y luego lo hago de nuevo (en llamadas RMI separadas), entonces los 2 objetos deserializados en el servidor serán no be ==.

Sin embargo, en una "serialización única", p. un único mensaje de cliente-servidor que es un gráfico que contiene el mismo objeto varias veces luego de la deserialización, la identidad es preservada.

Para el primer caso, se puede, sin embargo, proporcionar una implementación del método readResolve con el fin de garantizar que se devuelve la instancia correcta (por ejemplo en el typesafe enum patrón). readResolve es un método privado al que la JVM llamará en un objeto Java deserializado, lo que le brinda la oportunidad de devolver una instancia diferente. Por ejemplo, esta es la forma en la TimeUnitenum pudo ejecutarse antes de añadir enum 's de la lengua:

public class TimeUnit extends Serializable { 

    private int id; 
    public TimeUnit(int i) { id = i; } 
    public static TimeUnit SECONDS = new TimeUnit(0); 

    //Implement method and return the relevant static Instance 
    private Object readResolve() throws ObjectStreamException { 
     if (id == 0) return SECONDS; 
     else return this; 
    } 
} 

.

+1

Gracias. Usar 'readResolve' es exactamente lo que quería evitar ya que la contabilidad sería mucho más complicada que con el patrón de ensafe tipo seguro en mi caso. Por suerte para mí, solo estoy interesado en la serialización única. –

Cuestiones relacionadas