En caso de que una clase abstracta tener un serialVersionUID
-
23-08-2019 - |
Pregunta
En Java, si una clase implementa Serializable, pero es abstracto, en caso de tener un serialVersionUID declarado largo, o hacer las subclases que sólo requieren?
En este caso, es de hecho la intención de que todas las subclases ocupan de serialización como el propósito del tipo se va a utilizar en RMI llama.
Solución
El serialVersionUID se proporciona para determinar la compatibilidad entre un objeto deseralized 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. Usted 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 no get propaga a las subclases. A la luz de esto, DO necesitan la 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. A continuación, cambiar el serialVersionUID en la clase base, comente todas las líneas en las que salvan el principal objeto (por lo que no guarda otra vez, lo que desea es cargar el viejo), y ejecutarlo de nuevo. Esto dará lugar a una excepción
java.io.InvalidClassException: Base; local class incompatible: stream classdesc serialVersionUID = 1, local class serialVersionUID = 2
Otros consejos
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 la versión de serie de identificación o corre el riesgo de errores de serialización-des cuando la misma compilación .class no está en las JVM de servidor y cliente.
Hay otras opciones si usted está tratando de hacer algo de fantasía. No estoy seguro de lo que quiere decir con "es la intención de las clases sub ...". ¿Vas a escribir métodos de serialización personalizado (por ejemplo. WriteObject, readObject)? Si es así, hay otras opciones para hacer frente a una superclase.
Vea: http://java.sun.com/javase/6 /docs/api/java/io/Serializable.html
HTH Tom
En realidad, señalando de enlace de Tom si serialVersionID
que falta 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 declara explícitamente una serialVersionUID, entonces el tiempo de ejecución de serialización calculará una serialVersionUID valor por defecto para esa clase en base a varios aspectos de la clase ...
Esto hace que las cosas aún más complicadas que tienen diferentes versiones de JRE.
Conceptualmente, los datos serializados este aspecto:
subClassData(className + version + fieldNames + fieldValues)
parentClassData(className + version + fieldNames + fieldValues)
... (up to the first parent, that implements Serializable)
Así que cuando deserializar, falta de coincidencia en la versión en cualquiera de las clases en la jerarquía hace que la deserialización a fallar. Nada se almacena para las interfaces, así que no hay necesidad de especificar la versión de ellos.
Respuesta: sí, sí es necesario para proporcionar serialVersionUID
en la clase abstracta de base también. Incluso si no tiene campos (className + version
se almacenan incluso si no hay campos).
También tenga en cuenta lo siguiente:
- Si la clase no tiene un campo, que se encuentra en los datos serializados (un campo eliminado), se ignora.
- Si la clase tiene un campo, que no está presente en los datos serializados (un campo nuevo), que está establecido en 0 / falso / null (no al valor por defecto, como cabría esperar).
- si un campo de tipo de datos cambia, el valor deserializado debe poder asignar al nuevo tipo. P.ej. si tuviera un campo
Object
con valorString
, cambiando el tipo de campo aString
tendrá éxito, pero cambiando aInteger
no lo hará. Sin embargo, el cambio de campo deint
along
no funcionará, aunque se puede asignar un valor aint
long
variable. - Si subclase ya no se extiende la clase padre, la que se extiende en los datos serializados, se ignora (como en el caso 1).
- Si una subclase ahora se extiende una clase, que no se encuentra en los datos serializados, campos de clase de padres son restauradas con 0 / valor falso / null (como en el caso 2).
En palabras sencillas: se puede cambiar el orden de los campos, agregar y quitar ellos, incluso cambiar la jerarquía de clases. No se debe cambiar el nombre de los campos o clases (que no fallará, pero los valores no se deserializado). No se puede cambiar el tipo de campos con tipo primitivo, y se puede cambiar los campos de tipo de referencia siempre que el nuevo tipo es asignable de todos los valores.
Nota:. Si la clase base no implementa Serializable y sólo la subclase, a continuación, los campos de la clase base se comportaría como transient