2011-12-01 13 views
40

Tengo una clase pública, que implementa Serializable, que se extiende por varias otras clases. Solo esas subclases se serializaron antes, nunca la superclase.serialización Java - java.io.InvalidClassException clase local incompatible

La superclase había definido un serialVersionUID.

no estoy seguro si importa, pero no fue marcada privada, sino que sólo tenía la protección por defecto - se podría decir que era el paquete protegido

static final long serialVersionUID = -7588980448693010399L; 

El super clase, ni ninguna de las subclases, sin embargo implementaron readObject o writeObject, y ninguna de las subclases tenía un serialVersionUID explícitamente definido. Pensé que uno definido en la superclase sería suficiente.

A pesar de todo, las cosas estaban bien en cuanto a la lectura de objetos previamente serializados hasta que se añadiera una nueva variable de instancia, List/ArrayList, junto con un nuevo método a la superclase, y algunas variables de instancia privadas una de sus subclases.

Ahora, al intentar leer objetos previamente serializados, se está lanzando una excepción. Uno similar a esto:

com.SomeCompany.SomeSubClass; local class incompatible: stream classdesc serialVersionUID = 1597316331807173261, local class serialVersionUID = -3344057582987646196 

Asumo esto se deben a que el serialVersionUID por defecto, que se utilizó porque no declaró en una cualquiera de las subclases, ahora ha cambiado debido a los cambios en la superclase y una subclase.

Se agradecerán sugerencias sobre cómo salir de este dilema. Asumo que necesito implementar readObject y writeObject, pero aparte de invocar defaultReadObject() y defaultWriteObject(), no estoy exactamente seguro de lo que debo hacer. Tampoco sé si necesito agregar serialVerisonUIDs a todas las subclases o si readObject y writeObject deben ser implementados por cada subclase, o si puedo implementarlos una vez, suponiendo que lo necesito en absoluto, en la superclase.

Respuesta

36

@DanielChapman da una buena explicación de serialVersionUID, pero no hay solución. la solución es esta: ejecute el programa serialver en todas sus antiguas clases. ponga estos valores serialVersionUID en sus versiones actuales de las clases. siempre que las clases actuales sean compatibles en serie con las versiones anteriores, debería estar bien. (Tenga en cuenta para el futuro código: usted debe siempre tener un serialVersionUID en todosSerializable clases)

si las nuevas versiones son no serie compatible, entonces usted tiene que hacer un poco de magia con una implementación personalizada readObject (se solo necesitaría un writeObject personalizado si intentara escribir nuevos datos de clase que serían compatibles con el código anterior). En general, agregar o eliminar campos de clase no hace que una clase sea incompatible. cambiar el tipo de campos existentes generalmente lo hará.

Por supuesto, incluso si la nueva clase es serie compatible, es posible que todavía quiere una costumbre readObject aplicación. es posible que desee esto si desea completar cualquier campo nuevo que falte en los datos guardados de las versiones anteriores de la clase (por ejemplo, tiene un nuevo campo de Lista que desea inicializar en una lista vacía al cargar los datos de la clase anterior).

+4

... o si no tiene las clases antiguas, tome el antiguo serialVersionUID del mensaje de excepción, repita según sea necesario. –

+0

Su respuesta y [truthealitiy's] (http://stackoverflow.com/a/10378907/712526) combinados contienen todos los detalles relevantes. Tal vez las preguntas podrían fusionarse? – jpaugh

22

La respuesta corta aquí es que el ID de serie se calcula a través de un hash si no lo especifica. (Los miembros estáticos no son heredados, son estáticos, solo (1) y pertenecen a la clase).

http://docs.oracle.com/javase/6/docs/platform/serialization/spec/class.html

El método getSerialVersionUID devuelve el serialVersionUID de esta clase . Consulte la Sección 4.6, "Transmitir identificadores únicos". Si no es especificado por la clase, el valor devuelto es un hash calculado a partir del nombre de la clase , interfaces, métodos y campos utilizando el algoritmo Secure Hash (SHA) según lo define el Instituto Nacional de Estándares.

Si modifica una clase o su jerarquía, su hash será diferente. Ésto es una cosa buena. Tus objetos son diferentes ahora que tienen diferentes miembros. Como tal, si lo vuelve a leer desde su forma serializada, de hecho es un objeto diferente, por lo tanto, la excepción.

La respuesta larga es que la serialización es extremadamente útil, pero probablemente no debería usarse para la persistencia a menos que no haya otra manera de hacerlo. Es un camino peligroso específicamente por lo que estás experimentando. Debería considerar una base de datos, XML, un formato de archivo y probablemente una JPA u otra estructura de persistencia para un proyecto Java puro.

12

Para mí, olvidé agregar la identificación de serie predeterminada.

private static final long serialVersionUID = 1L; 
+0

+1; en principio, se agrega automáticamente al crear un nuevo archivo de "clase de entidad" _de Netbeans_. – Omar

4

Esto funcionó para mí:

Si usted escribió su clase de objeto serializado en un archivo, y luego hizo algunos cambios en un archivo y lo compiló, y luego intenta leer un objeto, entonces esto va a suceder .

Por lo tanto, escriba de nuevo los objetos necesarios en el archivo si una clase se modifica y vuelve a compilar.

PD: Esto NO es una solución; estaba destinado a ser una solución.

+0

No es necesario, o perder todas las serializaciones existentes. Ver las otras respuestas – EJP

+0

Esto fue pensado para ser una solución, no una solución. – skrtbhtngr

Cuestiones relacionadas