Serializzazione Java – class locale java.io.InvalidClassException incompatibile

Ho una class pubblica, che implementa Serializable, che è estesa da più altre classi. Solo quelle sottoclassi furono mai serializzate prima – mai la super class.

La super class aveva definito un serialVersionUID.

Non sono sicuro che sia importante, ma non è stato contrassegnato come privato, ma ha solo la protezione predefinita – potresti dire che è stato protetto da pacchetti

static final long serialVersionUID = -7588980448693010399L; 

La super class, né alcuna delle sottoclassi, tuttavia implementava readObject o writeObject, e nessuna delle sottoclassi aveva un serialVersionUID esplicitamente definito. Ho pensato che uno definito nella superclass sarebbe stato sufficiente.

Nonostante tutto ciò, le cose andavano bene per la lettura degli oggetti precedentemente serializzati fino a quando una nuova variabile di istanza, una List / ArrayList, insieme a un nuovo metodo venivano aggiunte alla super class, e alcune variabili di istanza private venivano aggiunte a una delle sue sottoclassi .

Ora quando si tenta di leggere gli oggetti precedentemente serializzati, viene generata un’eccezione. Uno simile a questo:

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

Presumo che ciò sia causato dal fatto che il serialVersionUID predefinito, che è stato utilizzato perché non ne ho dichiarato uno in nessuna delle sottoclassi, ora è cambiato a causa delle modifiche nella superclass e in una sottoclass.

I suggerimenti su come uscire da questo dilemma sarebbero apprezzati. Suppongo di dover implementare readObject e writeObject, ma a parte il richiamo di defaultReadObject () e di defaultWriteObject (), non sono esattamente sicuro di cosa devo fare. Né so se devo aggiungere serialVerisonUIDs a tutte le sottoclassi o se readObject e writeObject devono essere implementati da ciascuna sottoclass, o se posso solo implementarli una volta, supponendo che sia necessario, nella superclass.

@DanielChapman fornisce una buona spiegazione di serialVersionUID, ma nessuna soluzione. la soluzione è questa: serialver programma serialver su tutte le tue vecchie classi. inserisci questi valori serialVersionUID nelle versioni correnti delle classi. Finché le classi attuali sono seriali compatibili con le vecchie versioni, dovresti stare bene. (nota per il codice futuro: dovresti sempre avere un serialVersionUID su tutte le classi Serializable )

se le nuove versioni non sono seriali compatibili, allora devi fare un po ‘di magia con un’implementazione readObject personalizzata (avresti bisogno solo di un writeObject personalizzato se stavi cercando di scrivere nuovi dati di class che sarebbero compatibili con il vecchio codice). in generale, l’aggiunta o la rimozione di campi di class non rende incompatibile una class seriale. di solito cambierà il tipo di campi esistenti.

Naturalmente, anche se la nuova class è compatibile con la serie, è ansible che si desideri comunque un’implementazione readObject personalizzata. lo si può desiderare se si desidera compilare nuovi campi mancanti nei dati salvati da vecchie versioni della class (ad esempio, si ha un nuovo campo Elenco che si desidera inizializzare in una lista vuota quando si caricano dati di vecchia class).

La risposta breve qui è che l’ID seriale viene calcolato tramite un hash se non lo si specifica. (I membri statici non sono ereditati, sono statici, c’è solo (1) e appartiene alla class).

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

Il metodo getSerialVersionUID restituisce serialVersionUID di questa class. Fare riferimento alla Sezione 4.6, “Stream Unique Identifiers”. Se non specificato dalla class, il valore restituito è un hash calcolato dal nome, dalle interfacce, dai metodi e dai campi della class utilizzando Secure Hash Algorithm (SHA) come definito dal National Institute of Standards.

Se modifichi una class o la sua gerarchia il tuo hash sarà diverso. Questa è una buona cosa. I tuoi oggetti sono diversi ora che hanno membri diversi. In quanto tale, se lo leggi di nuovo dalla sua forma serializzata, si tratta in realtà di un object diverso, quindi l’eccezione.

La risposta lunga è che la serializzazione è estremamente utile, ma probabilmente non dovrebbe essere usata per la persistenza a meno che non ci sia un altro modo per farlo. È un percorso pericoloso in particolare a causa di ciò che stai vivendo. Dovresti considerare un database, XML, un formato di file e probabilmente un JPA o altra struttura di persistenza per un puro progetto Java.

Per quanto mi riguarda, ho dimenticato di aggiungere l’ID seriale predefinito.

 private static final long serialVersionUID = 1L; 

Questo ha funzionato per me:

Se hai scritto il tuo object di class Serialized in un file, poi hai apportato alcune modifiche al file e lo hai compilato, e poi provi a leggere un object, questo succederà.

Quindi, scrivi gli oggetti necessari per file di nuovo se una class viene modificata e ricompilata.

PS: NON è una soluzione; doveva essere una soluzione.