2009-05-21 17 views
59

En java, si una clase implementa Serializable pero es abstracta, ¿debería tener un serialVersionUID long declarado o las subclases solo lo requieren?En caso de que una clase abstracta tenga un serialVersionUID

En este caso, es de hecho la intención de que todas las sub clases se ocupen de la serialización ya que el propósito del tipo es ser utilizado en llamadas RMI.

+3

Sigo empezando a escribir una respuesta, y luego me doy cuenta de que no sé exactamente, aunque tengo una corazonada. +1 por una pregunta que no puedo responder. –

Respuesta

40

El serialVersionUID se proporciona para determinar la compatibilidad entre un objeto desescalado y la versión actual de la clase. Como tal, no es realmente necesario en la primera versión de una clase, o en este caso, en una clase base abstracta. Nunca tendrá una instancia de esa clase abstracta para serializar/deserializar, por lo que no necesita un serialVersionUID.

(Por supuesto, esto generará una advertencia del compilador, que desea quitarse de encima, ¿verdad?)

Resulta comentario de James es correcta. El serialVersionUID de una clase base abstracta hace que se propague a subclases. A la luz de eso, usted do necesita el serialVersionUID en su clase base.

El código de prueba:

import java.io.Serializable; 

public abstract class Base implements Serializable { 

    private int x = 0; 
    private int y = 0; 

    private static final long serialVersionUID = 1L; 

    public String toString() 
    { 
     return "Base X: " + x + ", Base Y: " + y; 
    } 
} 



import java.io.FileInputStream; 
import java.io.FileOutputStream; 
import java.io.ObjectInputStream; 
import java.io.ObjectOutputStream; 

public class Sub extends Base { 

    private int z = 0; 

    private static final long serialVersionUID = 1000L; 

    public String toString() 
    { 
     return super.toString() + ", Sub Z: " + z; 
    } 

    public static void main(String[] args) 
    { 
     Sub s1 = new Sub(); 
     System.out.println(s1.toString()); 

     // Serialize the object and save it to a file 
     try { 
      FileOutputStream fout = new FileOutputStream("object.dat"); 
      ObjectOutputStream oos = new ObjectOutputStream(fout); 
      oos.writeObject(s1); 
      oos.close(); 
     } catch (Exception e) { 
      e.printStackTrace(); 
     } 

     Sub s2 = null; 
     // Load the file and deserialize the object 
     try { 
      FileInputStream fin = new FileInputStream("object.dat"); 
      ObjectInputStream ois = new ObjectInputStream(fin); 
      s2 = (Sub) ois.readObject(); 
      ois.close(); 
     } catch (Exception e) { 
      e.printStackTrace(); 
     } 

     System.out.println(s2.toString()); 
    } 
} 

Ejecutar el principal Sub vez para conseguirlo para crear y guardar un objeto. Luego cambie el serialVersionUID en la clase Base, comente las líneas en main que guardan el objeto (para que no lo guarde de nuevo, solo quiera cargar el anterior) y ejecútelo nuevamente. Esto dará lugar a una excepción

java.io.InvalidClassException: Base; local class incompatible: stream classdesc serialVersionUID = 1, local class serialVersionUID = 2 
+0

Buena respuesta ... @SuppressWarnings ("serial") suprimirá el mensaje de advertencia –

+1

@Ryan: Gracias, pero normalmente trato las advertencias como errores y las trato directamente. –

+1

... pero entiendo que no todos son tan dogmáticos al respecto como yo, así que agradezco su comentario. –

6

Sí, en general, por la misma razón que cualquier otra clase necesita un ID de serie - para evitar que se está generado por ella. Básicamente, cualquier clase (no interfaz) que implementa serializable debe definir el ID de la versión en serie o corre el riesgo de errores de serialización cuando la misma compilación .class no está en el servidor y las JVM del cliente.

Hay otras opciones si está intentando hacer algo elegante. No estoy seguro de a qué te refieres con "es la intención de las subclases ...". ¿Vas a escribir métodos de serialización personalizados (por ejemplo, writeObject, readObject)? Si es así, hay otras opciones para lidiar con una súper clase.

véase: http://java.sun.com/javase/6/docs/api/java/io/Serializable.html

HTH Tom

2

En realidad, señalando de enlace de Tom si falta serialVersionID se presenta en realidad por la serialización en tiempo de ejecución es decir, no durante la compilación

Si una clase serializable no lo hace declarar explícitamente un serialVersionUID, luego el tiempo de ejecución de serialización calculará un valor serialVersionUID por defecto de ss basado en varios aspectos de la clase ...

Esto hace las cosas aún más complicadas teniendo diferentes versiones de JRE.

0

Conceptualmente, los datos serializados tener este aspecto:

subClassData(className + version + fieldNames + fieldValues) 
parentClassData(className + version + fieldNames + fieldValues) 
... (up to the first parent, that implements Serializable) 

Así que al deserializar, falta de coincidencia en la versión en cualquiera de las clases en la jerarquía hace que la deserialización falle. No se almacena nada para las interfaces, por lo que no es necesario especificar la versión para ellas.

Respuesta: sí, también debe proporcionar serialVersionUID en la clase abstracta base. Incluso si no tiene campos (className + version se almacenan incluso si no hay campos).

También tenga en cuenta lo siguiente:

  1. Si la clase no tiene un campo, que se encuentra en los datos serializados (un campo eliminado), se ignora.
  2. Si la clase tiene un campo, que no está presente en los datos serializados (un nuevo campo), se establece en 0/falso/nulo (no en el valor predeterminado como cabría esperar).
  3. si un campo cambia el tipo de datos, el valor deserializado debe ser asignable al nuevo tipo. P.ej. si tenía un campo Object con String valor, cambiar el tipo de campo a String tendrá éxito, pero cambiar a Integer no lo hará. Sin embargo, cambiar el campo de int a long no funcionará, aunque puede asignar int a long variable.
  4. Si la subclase ya no extiende la clase principal, que se extiende en los datos serializados, se ignora (como en el caso 1).
  5. Si una subclase ahora amplía una clase, que no se encuentra en los datos serializados, los campos de clase primaria se restauran con 0/falso/valor nulo (como en el caso 2).

En palabras simples: puede reordenar campos, agregarlos y eliminarlos, incluso cambiar la jerarquía de clases. No debe cambiar el nombre de los campos o las clases (no fallará, pero los valores no se deserializarán). No puede cambiar el tipo de campo con el tipo primitivo, y puede cambiar los campos de tipo de referencia, siempre que el nuevo tipo se pueda asignar a todos los valores.

Nota: si la clase base no implementa Serializable y solo lo hace la subclase, los campos de la clase base se comportarían como transient.

Cuestiones relacionadas