Serialización Java – java.io.InvalidClassException clase local incompatible

Tengo una clase pública, que implementa Serializable, que se extiende por varias otras clases. Solo esas subclases se serializaron antes, nunca la superclase.

La superclase había definido un serialVersionUID.

No estoy seguro de si importa, pero no se marcó como privado, sino que simplemente tenía la protección predeterminada, podría decirse que estaba protegido por paquete

static final long serialVersionUID = -7588980448693010399L; 

La superclase, ni ninguna de las subclases, sin embargo implementó 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 agregó una nueva variable de instancia, List / ArrayList, junto con un nuevo método a la superclase, y se agregaron algunas variables de instancia privadas a una de sus subclases .

Ahora cuando intenta leer objetos previamente serializados, se lanza una excepción. Uno similar a esto:

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

Supongo que esto se debe a que el serialVersionUID predeterminado, que se utilizó porque no declare uno en ninguna de las subclases, ahora ha cambiado debido a los cambios en la superclase y una subclase.

Las sugerencias sobre cómo salir de este dilema serían apreciadas. 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.

@DanielChapman da una buena explicación de serialVersionUID, pero no hay solución. la solución es esta: ejecuta el progtwig serialver en todas tus clases antiguas . pon estos valores serialVersionUID en tus versiones actuales de las clases. siempre que las clases actuales sean compatibles en serie con las versiones anteriores, debería estar bien. (Nota para el código futuro: siempre debe tener un serialVersionUID en todas las clases de Serializable )

si las nuevas versiones no son compatibles en serie, entonces necesitas hacer algo mágico con una implementación de readObject personalizada (solo necesitarías un writeObject personalizado si estuvieras tratando de 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 compatible en serie, es posible que desee una implementación de readObject personalizada. Puede desear esto si desea completar cualquier campo nuevo que falte en los datos guardados de versiones anteriores de la clase (por ejemplo, tiene un nuevo campo de Lista que desea inicializar en una lista vacía cuando carga datos de clases anteriores).

La respuesta breve aquí es que la identificación en 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 la clase no lo especifica, el valor devuelto es un hash calculado a partir del nombre, las interfaces, los métodos y los campos de la clase, utilizando el Algoritmo Hash Seguro (SHA) tal como 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.

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

 private static final long serialVersionUID = 1L; 

Esto funcionó para mí:

Si escribió su objeto de clase Serialized en un archivo, luego realizó algunos cambios en el archivo y lo compiló, y luego intenta leer un objeto, entonces esto sucederá.

Por lo tanto, escriba los objetos necesarios para volver a archivar si una clase se modifica y vuelve a comstackr.

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