java generator - Che cos'è un serialVersionUID e perché dovrei usarlo?




serializzazione calculate (18)

Eclipse emette avvisi quando manca un serialVersionUID .

La classe serializzabile Foo non dichiara un campo seriale serialVersionUID finale di tipo lungo

Cos'è serialVersionUID e perché è importante? Si prega di mostrare un esempio in cui manca serialVersionUID causerà un problema.


Answers

Che cos'è un serialVersionUID e perché dovrei usarlo?

SerialVersionUID è un identificatore univoco per ogni classe, JVM utilizza per confrontare le versioni della classe assicurando che sia stata utilizzata la stessa classe durante il caricamento della serializzazione durante la deserializzazione.

Specificarne uno dà più controllo, anche se JVM ne genera uno se non lo si specifica. Il valore generato può variare tra diversi compilatori. Inoltre, a volte vuoi solo per qualche motivo proibire la deserializzazione di vecchi oggetti serializzati [ backward incompatibility ], e in questo caso devi solo cambiare serialVersionUID.

I javadoc per Serializable dicono :

il calcolo predefinito di serialVersionUID è estremamente sensibile ai dettagli della classe che possono variare a seconda delle implementazioni del compilatore e può pertanto provocare InvalidClassException s inattesi durante la deserializzazione.

Pertanto, è necessario dichiarare serialVersionUID perché ci dà più controllo .

Questo articolo ha alcuni punti positivi sull'argomento.


Sarebbe bello se CheckStyle potesse verificare che il serialVersionUID su una classe che implementa Serializable abbia un buon valore, cioè che corrisponda a ciò che il generatore di id della versione seriale produrrebbe. Se si dispone di un progetto con molti DTO serializzabili, ad esempio, ricordare di eliminare il serialVersionUID esistente e rigenerarlo è un problema, e attualmente l'unico modo (che io sappia) per verificarlo è quello di rigenerare per ogni classe e confrontare con quello vecchio. Questo è molto molto doloroso.


Ogni volta che un oggetto viene serializzato, l'oggetto viene timbrato con un numero ID di versione per la classe dell'oggetto. Questo ID è chiamato serialVersionUID ed è calcolato in base alle informazioni sulla struttura della classe. Supponete di aver creato una classe Employee e che abbia l'ID di versione # 333 (assegnato da JVM), Ora quando serializzerete l'oggetto di quella classe (Suppose Employee object), JVM assegnerà l'UID ad esso come # 333.

Considera una situazione: in futuro dovrai modificare o cambiare la tua classe e in tal caso quando la modifichi, JVM assegnerà un nuovo UID (Supponiamo # 444). Ora, quando si tenta di deserializzare l'oggetto dipendente, JVM confronterà l'ID versione dell'oggetto (Employee) dell'oggetto serializzato (n. 333) con quello della classe cioè # 444 (Poiché è stato modificato). A confronto, JVM troverà che entrambi gli UID di versione sono diversi e quindi la deserializzazione fallirà. Quindi se serialVersionID per ogni classe è definito dal programmatore stesso. Sarà lo stesso anche se la classe si evolve in futuro e quindi JVM troverà sempre che la classe è compatibile con l'oggetto serializzato anche se la classe è cambiata. Per ulteriori informazioni è possibile consultare il capitolo 14 di HEAD FIRST JAVA.


Puoi dire a Eclipse di ignorare questi avvisi di serialVersionUID:

Finestra> Preferenze> Java> Compilatore> Errori / Avvisi> Potenziali problemi di programmazione

Nel caso in cui non lo sapessi, ci sono molti altri avvisi che puoi abilitare in questa sezione (o anche alcuni di quelli segnalati come errori), molti sono molto utili:

  • Potenziali problemi di programmazione: Possibile assegnazione booleana accidentale
  • Potenziali problemi di programmazione: accesso al puntatore nullo
  • Codice non necessario: la variabile locale non viene mai letta
  • Codice non necessario: controllo null ridondante
  • Codice non necessario: cast non necessario o 'instanceof'

e tanti altri.


Per capire il significato del campo serialVersionUID, si dovrebbe capire come funziona la serializzazione / deserializzazione.

Quando un oggetto di classe Serializable è serializzato, Java Runtime associa un numero di versione seriale (chiamato come serialVersionUID) con questo oggetto serializzato. Al momento della deserializzazione di questo oggetto serializzato, Java Runtime corrisponde a serialVersionUID dell'oggetto serializzato con serialVersionUID della classe. Se entrambi sono uguali, solo procede con l'ulteriore processo di deserializzazione, altrimenti lancia InvalidClassException.

Quindi concludiamo che per rendere efficace il processo di serializzazione / deserializzazione il serialVersionUID dell'oggetto serializzato deve essere equivalente al serialVersionUID della classe. Nel caso in cui il programmatore specifichi esplicitamente il valore serialVersionUID nel programma, lo stesso valore sarà associato all'oggetto serializzato e alla classe, indipendentemente dalla piattaforma di serializzazione e deserializzazione (per esempio la serializzazione potrebbe essere eseguita su piattaforme come Windows usando sole o MS JVM e Deserialization potrebbero trovarsi su diverse piattaforme Linux usando Zing JVM).

Ma nel caso in cui serialVersionUID non venga specificato dal programmatore, mentre esegue Serialization \ DeSerialization di qualsiasi oggetto, Java runtime utilizza il proprio algoritmo per calcolarlo. Questo algoritmo di calcolo serialVersionUID varia da un JRE a un altro. È anche possibile che l'ambiente in cui l'oggetto è serializzato utilizzi un JRE (es: SUN JVM) e l'ambiente in cui avviene la deserializzazione si sta utilizzando Linux Jvm (zing). In questi casi, serialVersionUID associato all'oggetto serializzato sarà diverso rispetto a serialVersionUID della classe calcolata in ambiente di deserializzazione. A sua volta la deserializzazione non avrà successo. Per evitare tali situazioni / problemi, il programmatore deve sempre specificare serialVersionUID della classe Serializable.


Perché usare la classe SerialVersionUIDinterna Serializablein Java?

Durante serialization, Java runtime crea un numero di versione per una classe, in modo che possa declassificarlo in un secondo momento. Questo numero di versione è noto come SerialVersionUIDin Java.

SerialVersionUIDè usato per la versione dei dati serializzati. È possibile declassare una classe solo se SerialVersionUIDcorrisponde all'istanza serializzata. Quando non lo dichiariamo SerialVersionUIDnella nostra classe, Java runtime lo genera per noi ma non è raccomandato. Si consiglia di dichiarare SerialVersionUIDcome private static final longvariabile per evitare il meccanismo predefinito.

Quando si dichiara una classe come Serializableimplementando l'interfaccia marker java.io.Serializable, Java runtime persiste l'istanza di tale classe nel disco utilizzando il meccanismo di serializzazione predefinito, a condizione che non si sia personalizzato il processo utilizzando l' Externalizableinterfaccia.

vedi anche Perché usare SerialVersionUID all'interno della classe Serializable in Java

Codice: javassist.SerialVersionUID


Come per un esempio in cui il serialVersionUID mancante potrebbe causare un problema:

Sto lavorando a questa applicazione Java EE che è composta da un modulo Web che utilizza un modulo EJB . Il modulo web chiama il modulo EJB remoto e passa un POJO che implementa Serializable come argomento.

POJO's classe di questo POJO's stata impacchettata all'interno del jar EJB e all'interno del proprio jar nel WEB-INF / lib del modulo web. In realtà sono della stessa classe, ma quando impacchetta il modulo EJB, spacchetto questo jar di POJO per comprarlo insieme al modulo EJB.

La chiamata al EJB stava fallendo con l'Eccezione di seguito perché non avevo dichiarato il suo serialVersionUID :

Caused by: java.io.IOException: Mismatched serialization UIDs : Source
 (Rep.
 IDRMI:com.hordine.pedra.softbudget.domain.Budget:5CF7CE11E6810A36:04A3FEBED5DA4588)
 = 04A3FEBED5DA4588 whereas Target (Rep. ID RMI:com.hordine.pedra.softbudget.domain.Budget:7AF5ED7A7CFDFF31:6227F23FA74A9A52)
 = 6227F23FA74A9A52

Questa domanda è molto ben documentata in Java efficace da Joshua Bloch. Un libro molto buono e assolutamente da leggere. Illustrerò alcuni dei seguenti motivi:

Il runtime di serializzazione si presenta con un numero chiamato Versione seriale per ogni classe serializzabile. Questo numero è chiamato serialVersionUID. Ora c'è un po 'di matematica dietro questo numero e esce in base ai campi / metodi che sono definiti nella classe. Per la stessa classe viene generata sempre la stessa versione. Questo numero viene utilizzato durante la deserializzazione per verificare che il mittente e il destinatario di un oggetto serializzato abbiano classi caricate per quell'oggetto compatibili con la serializzazione. Se il destinatario ha caricato una classe per l'oggetto che ha un serialVersionUID diverso da quello della classe del mittente corrispondente, la deserializzazione produrrà una InvalidClassException.

Se la classe è serializzabile, è anche possibile dichiarare il proprio serialVersionUID esplicitamente dichiarando un campo denominato "serialVersionUID" che deve essere statico, definitivo e di tipo lungo. La maggior parte degli IDE come Eclipse ti aiutano a generare quella lunga stringa.


Generalmente uso serialVersionUID in un contesto: quando so che lascerà il contesto della Java VM.

Lo saprei quando uso ObjectInputStream e ObjectOutputStream per la mia applicazione o se so che una libreria / framework che uso lo userà. Il serialVersionID garantisce che diverse VM Java di versioni o venditori diversi interagiscano correttamente o se vengono archiviate e recuperate al di fuori della VM, ad esempio HttpSession i dati della sessione possono rimanere anche durante il riavvio e l'aggiornamento del server delle applicazioni.

Io uso per tutti gli altri casi

@SuppressWarnings("serial")

poiché la maggior parte delle volte il serialVersionUID predefinito è sufficiente. Ciò include Exception , HttpServlet .


I dati del campo rappresentano alcune informazioni memorizzate nella classe. La classe implementa l'interfaccia Serializable , quindi eclipse viene automaticamente offerto per dichiarare il campo serialVersionUID . Iniziamo con il valore 1 impostato lì.

Se non vuoi che questo avviso venga, usa questo:

@SuppressWarnings("serial")

Se non avrai mai bisogno di serializzare i tuoi oggetti su array di byte e inviarli / memorizzarli, non dovrai preoccuparti di questo. Se lo fai, allora devi considerare il tuo serialVersionUID poiché il deserializzatore dell'oggetto lo abbinerà alla versione dell'oggetto che ha il suo classloader. Maggiori informazioni su questo argomento nella specifica del linguaggio Java.


La domanda originale ha chiesto "perché è importante" e "esempio" in cui questo Serial Version ID sarebbe utile. Bene, ho trovato uno.

Supponi di creare una classe Car , di istanziarla e di scriverla su un flusso di oggetti. L'oggetto appiattito si trova nel file system per un po 'di tempo. Nel frattempo, se la classe Car viene modificata aggiungendo un nuovo campo. In seguito, quando si tenta di leggere (cioè deserializzare) l'oggetto Car appiattito, si ottiene java.io.InvalidClassException - poiché a tutte le classi serializzabili viene automaticamente assegnato un identificatore univoco. Questa eccezione viene generata quando l'identificatore della classe non è uguale all'identificatore dell'oggetto appiattito. Se ci pensi veramente, l'eccezione viene generata a causa dell'aggiunta del nuovo campo. È possibile evitare che questa eccezione venga generata controllando il controllo delle versioni dichiarando un serialVersionUID esplicito. C'è anche un piccolo vantaggio prestazionale nel dichiarare esplicitamente il tuo serialVersionUID (perché non deve essere calcolato). Pertanto, è consigliabile aggiungere il proprio serialVersionUID alle classi Serializable non appena le crei come mostrato di seguito:

public class Car {
static final long serialVersionUID = 1L; //assign a long value
}

Non preoccupatevi, il calcolo predefinito è veramente buono e sufficiente per il 99,9999% dei casi. E se ti imbatti in problemi, puoi - come già affermato - introdurre gli UID come il bisogno di arrise (che è altamente improbabile)


Non posso perdere questa occasione per collegare il libro di Josh Bloch, Effective Java (2nd Edition). Il capitolo 11 è una risorsa indispensabile sulla serializzazione Java.

Per Josh, l'UID generato automaticamente viene generato sulla base di un nome di classe, interfacce implementate e tutti i membri pubblici e protetti. Modificando uno di questi in qualsiasi modo cambierà serialVersionUID . Quindi non è necessario pasticciarli solo se si è certi che non verrà serializzata più di una versione della classe (tra processi o recuperati dallo storage in un momento successivo).

Se li ignori per ora, e scopri in seguito che hai bisogno di cambiare la classe in qualche modo, ma mantieni la compatibilità con la vecchia versione della classe, puoi usare lo strumento JDK serialver per generare serialVersionUID sulla vecchia classe ed impostare esplicitamente quello sulla nuova classe. (A seconda delle modifiche, potrebbe essere necessario implementare anche la serializzazione personalizzata aggiungendo i metodi writeObject e readObject : vedere javadoc Serializable o capitolo 11 precedente).


SerialVersionUID viene utilizzato per il controllo della versione dell'oggetto. puoi anche specificare serialVersionUID nel tuo file di classe. La conseguenza di non specificare serialVersionUID è che quando si aggiunge o modifica un campo qualsiasi nella classe, la classe già serializzata non sarà in grado di recuperare perché serialVersionUID generato per la nuova classe e per il vecchio oggetto serializzato sarà diverso. Il processo di serializzazione Java si basa sul serialVersionUID corretto per il ripristino dello stato dell'oggetto serializzato e genera java.io.InvalidClassException in caso di mancata corrispondenza di serialVersionUID

Per saperne di più: http://javarevisited.blogspot.com/2011/04/top-10-java-serialization-interview.html#ixzz3VQxnpOPZ


Se stai serializzando solo perché devi serializzare per il bene dell'implementazione (a chi importa se serializzi per un HTTPSession, per esempio ... se è memorizzato o meno, probabilmente non ti interessa la serializzazione di un oggetto modulo) , quindi puoi ignorarlo.

Se si utilizza effettivamente la serializzazione, è importante solo se si pianifica di archiviare e recuperare oggetti utilizzando direttamente la serializzazione. SerialVersionUID rappresenta la versione della tua classe e dovresti incrementarla se la versione corrente della tua classe non è retrocompatibile con la sua versione precedente.

La maggior parte delle volte, probabilmente non utilizzerai la serializzazione direttamente. In questo caso, genera un UID serializzabile predefinito facendo clic sull'opzione di correzione rapida e non preoccuparti.


Se si desidera modificare un numero enorme di classi che non hanno impostato serialVersionUID in primo luogo mantenendo la compatibilità con le classi precedenti, strumenti come IntelliJ Idea, Eclipse non sono sufficienti in quanto generano numeri casuali e non funzionano su un gruppo di file in un colpo solo. Vengo al seguente script bash (mi dispiace per gli utenti di Windows, considera l'acquisto di un Mac o la conversione a Linux) per modificare facilmente il problema serialVersionUID:

base_dir=$(pwd)                                                                  
src_dir=$base_dir/src/main/java                                                  
ic_api_cp=$base_dir/target/classes                                               

while read f                                                                     
do                                                                               
    clazz=${f//\//.}                                                             
    clazz=${clazz/%.java/}                                                       
    seruidstr=$(serialver -classpath $ic_api_cp $clazz | cut -d ':' -f 2 | sed -e 's/^\s\+//')
    perl -ni.bak -e "print $_; printf qq{%s\n}, q{    private $seruidstr} if /public class/" $src_dir/$f
done

si salva questo script, ad esempio add_serialVersionUID.sh ~ / bin. Quindi lo esegui nella directory principale del tuo progetto Maven o Gradle come:

add_serialVersionUID.sh < myJavaToAmend.lst

Questo .lst include l'elenco dei file java per aggiungere serialVersionUID nel seguente formato:

com/abc/ic/api/model/domain/item/BizOrderTransDO.java
com/abc/ic/api/model/domain/item/CardPassFeature.java
com/abc/ic/api/model/domain/item/CategoryFeature.java
com/abc/ic/api/model/domain/item/GoodsFeature.java
com/abc/ic/api/model/domain/item/ItemFeature.java
com/abc/ic/api/model/domain/item/ItemPicUrls.java
com/abc/ic/api/model/domain/item/ItemSkuDO.java
com/abc/ic/api/model/domain/serve/ServeCategoryFeature.java
com/abc/ic/api/model/domain/serve/ServeFeature.java
com/abc/ic/api/model/param/depot/DepotItemDTO.java
com/abc/ic/api/model/param/depot/DepotItemQueryDTO.java
com/abc/ic/api/model/param/depot/InDepotDTO.java
com/abc/ic/api/model/param/depot/OutDepotDTO.java

Questo script utilizza lo strumento JDK serialVer sotto cappa. Quindi assicurati che il tuo $ JAVA_HOME / bin sia nel PERCORSO.


Dubito che tu possa accelerare davvero la "sostituzione del personaggio". Per quanto riguarda la sostituzione delle espressioni regolari, è possibile compilare in anticipo le espressioni regolari





java serialization serialversionuid