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


2999

Eclipse emette avvisi quando serialVersionUIDmanca un.

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

Che cos'è serialVersionUIDe perché è importante? Si prega di mostrare un esempio in cui la mancanza serialVersionUIDcauserà un problema.


1
Trova una buona pratica su serialversionUID; dzone.com/articles/what-is-serialversionuid
ceyun

Risposte:


2290

I documenti per java.io.Serializableprobabilmente sono una spiegazione tanto valida quanto otterrai:

Il runtime di serializzazione associa a ciascuna classe serializzabile un numero di versione, chiamato a serialVersionUID, che viene utilizzato durante la deserializzazione per verificare che il mittente e il destinatario di un oggetto serializzato abbiano caricato classi per quell'oggetto compatibili rispetto alla serializzazione. Se il destinatario ha caricato una classe per l'oggetto che ha una diversa serialVersionUIDda quella della corrispondente classe del mittente, la deserializzazione si tradurrà in un InvalidClassException . Una classe serializzabile può dichiararsi serialVersionUIDesplicitamente dichiarando un campo denominato serialVersionUIDche deve essere statico, finale e di tipo long:

ANY-ACCESS-MODIFIER static final long serialVersionUID = 42L;

Se una classe serializzabile non dichiara esplicitamente a serialVersionUID, il runtime di serializzazione calcolerà un serialVersionUIDvalore predefinito per quella classe in base a vari aspetti della classe, come descritto nelle Specifiche di serializzazione degli oggetti Java (TM). Tuttavia, si consiglia vivamente che tutte le classi serializzabili dichiarino esplicitamente i serialVersionUIDvalori, poiché il serialVersionUIDcalcolo predefinito è altamente sensibile ai dettagli della classe che possono variare a seconda delle implementazioni del compilatore e possono quindi provocare imprevisti InvalidClassExceptionsdurante la deserializzazione. Pertanto, per garantire un serialVersionUIDvalore coerente tra le diverse implementazioni del compilatore java, una classe serializzabile deve dichiarare un serialVersionUIDvalore esplicito . Si consiglia inoltre vivamente che esplicitoserialVersionUIDle dichiarazioni utilizzano il modificatore privato ove possibile, poiché tali dichiarazioni si applicano solo ai serialVersionUIDcampi di classe che dichiarano immediatamente non sono utili come membri ereditati.


326
Quindi, quello che stai dicendo essenzialmente è che se un utente non capiva tutto il materiale sopra, ha detto che l'utente non dovrebbe preoccuparsi di preoccuparsi della serializzazione? Credo che tu abbia risposto al "come?" piuttosto che spiegare il "perché?". Io, per esempio, non capisco perché mi sono preoccupato di SerializableVersionUID.
Ziggy,

366
Il motivo è nel secondo paragrafo: se non si specifica esplicitamente serialVersionUID, un valore viene generato automaticamente, ma è fragile perché dipende dall'implementazione del compilatore.
Jon Skeet,

14
E perché Eclipse dice che ho bisogno di "private serial final serialVersionUID = 1L;" quando estendo la classe Exception?
John Merlino,

21
@JohnMerlino: Beh, non mi aspetto che ti dica che ne hai bisogno , ma potrebbe suggerirne uno per aiutarti a serializzare correttamente le eccezioni. Se non li serializzi, non hai davvero bisogno della costante.
Jon Skeet,

16
@JohnMerlino, per rispondere al motivo per cui parte della tua domanda: Eccezione implementa Serializable ed eclipse avverte che non hai impostato un serialVersionUID, che sarebbe una buona idea (se non vuoi serializzare la classe) per evitare i problemi che il post di JonSkeet lineamenti.
zpon,

475

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 no, probabilmente non ti interessa de-serializingun oggetto modulo), allora puoi ignoralo.

Se stai effettivamente utilizzando la serializzazione, è importante solo se prevedi di archiviare e recuperare oggetti utilizzando direttamente la serializzazione. IlserialVersionUID rappresenta la versione di classe, e si dovrebbe incrementarlo se la versione corrente della classe non è compatibile con la versione precedente.

Il più delle volte, probabilmente non userete direttamente la serializzazione. In questo caso, genera un valore predefinito SerialVersionUIDfacendo clic sull'opzione di correzione rapida e non preoccuparti.


78
Direi che se non si utilizza la serializzazione per l'archiviazione permanente, è necessario utilizzare @SuppressWarnings anziché aggiungere un valore. Raggruppa di meno la classe e preserva l'abilità del meccanismo serialVersionUID per proteggerti da modifiche incompatibili.
Tom Anderson,

25
Non vedo come l'aggiunta di una riga (annotazione @SuppressWarnings) rispetto a un'altra riga (ID serializzabile) "ingombra la classe di meno". E se non stai usando la serializzazione per l'archiviazione permanente, perché non dovresti semplicemente usare "1"? In questo caso non ti interesserebbe l'ID di generazione automatica.
MetroidFan2002,

71
@ MetroidFan2002: Penso che il punto di TomAnderson di serialVersionUIDproteggerti da modifiche incompatibili sia valido. Utilizzare @SuppressWarningsmeglio i documenti se non si desidera utilizzare la classe per l'archiviazione permanente.
AbdullahC

11
"Dovresti incrementarlo se la versione corrente della tua classe non è retrocompatibile con la sua versione precedente:" Dovresti prima esplorare l'ampio supporto di versioning degli oggetti di Serializzazione, (a) per assicurarti che la classe sia davvero incompatibile con la serializzazione, quale per la specifica è abbastanza difficile da raggiungere; (b) per provare uno schema come metodi personalizzati read / writeObject (), metodi readResolve / writeReplace (), dichiarazioni serializzabili dei campi, ecc., per assicurarsi che il flusso rimanga compatibile. Cambiare l'effettivo serialVersionUIDè l'ultima risorsa, un consiglio di disperazione.
Marchese di Lorne,

4
L'incremento @EJP di serialVersionUID viene illustrato quando l'autore iniziale della classe è stato introdotto esplicitamente. Direi che l'id seriale generato da jvm dovrebbe andare bene. questa è la migliore risposta che ho visto sulla serializzazione.
scambio eccessivo il

307

Non posso perdere l'occasione di collegare il libro di Josh Bloch Effective Java (2a edizione). Il capitolo 11 è una risorsa indispensabile sulla serializzazione Java.

Per Josh, l'UID generato automaticamente viene generato in base al nome di una classe, alle interfacce implementate e a tutti i membri pubblici e protetti. La modifica di uno di questi in alcun modo cambierà il serialVersionUID. Quindi non è necessario pasticciarli solo se si è certi che non verrà mai serializzata più di una versione della classe (attraverso processi o recuperata dalla memoria in un secondo momento).

Se per ora li ignori e scopri in seguito che devi cambiare la classe in qualche modo ma mantenere la compatibilità con la vecchia versione della classe, puoi usare il serialver dello strumento JDK per generare serialVersionUIDla vecchia classe e impostare esplicitamente che sulla nuova classe. (A seconda delle modifiche, potrebbe essere necessario implementare anche la serializzazione personalizzata aggiungendo writeObjecte readObjectmetodi: consultare Serializablejavadoc o il capitolo 11 di cui sopra).


33
Quindi ci si potrebbe preoccupare di SerializableVersionUID se si fosse preoccupati della compatibilità con le vecchie versioni di una classe?
Ziggy,

10
Sì, nel caso in cui la versione più recente cambi un membro pubblico in protetto, il SerializableVersionUID predefinito sarà diverso e genererà InvalidClassExceptions.
Chander Shivdasani,

3
nome classe, interfacce implementate, tutti i metodi pubblici e protetti, TUTTE le variabili di istanza.
Achow

31
Vale la pena notare che Joshua Bloch consiglia che per ogni classe serializzabile vale la pena specificare la versione seriale uid. Citazione dal capitolo 11: Indipendentemente dal modulo serializzato che scegli, dichiara un UID esplicito della versione seriale in ogni classe serializzabile che scrivi. Ciò elimina l'UID della versione seriale come potenziale fonte di incompatibilità (elemento 74). C'è anche un piccolo vantaggio in termini di prestazioni. Se non viene fornito alcun UID di versione seriale, è necessario un calcolo costoso per generarne uno in fase di esecuzione.
Ashutosh Jindal,

136

Puoi dire a Eclipse di ignorare questi avvisi serialVersionUID:

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

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

  • Potenziali problemi di programmazione: possibile assegnazione booleana accidentale
  • Potenziali problemi di programmazione: accesso 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 molti altri.


21
voto, ma solo perché il poster originale non sembra serializzare nulla. Se il poster dicesse "Sto serializzando questa cosa e ...", otterresti invece un voto verso il basso: P
John Gardner,

3
Vero - supponevo che l'interrogatore semplicemente non volesse essere avvertito
matt b

14
@Gardner -> d'accordo! Ma l'interrogante vuole anche sapere perché potrebbe non voler essere avvertito.
Ziggy,

8
L'interrogatore si preoccupa ovviamente del perché ci dovrebbe essere un UID. Quindi, semplicemente dirgli di ignorare l'avvertimento dovrebbe essere annullato.
cinqS

111

serialVersionUIDfacilita il controllo delle versioni dei dati serializzati. Il suo valore viene memorizzato con i dati durante la serializzazione. Durante la deserializzazione, viene verificata la stessa versione per vedere come i dati serializzati corrispondono al codice corrente.

Se si desidera eseguire la versione dei dati, si inizia normalmente con un valore serialVersionUIDpari a 0 e lo si annulla con ogni modifica strutturale alla classe che altera i dati serializzati (aggiungendo o rimuovendo campi non transitori).

Il meccanismo di deserializzazione incorporato ( in.defaultReadObject()) rifiuterà di deserializzare dalle vecchie versioni dei dati. Ma se vuoi puoi definire la tua funzione readObject () che può leggere vecchi dati. Questo codice personalizzato può quindi controllare serialVersionUIDper sapere in quale versione si trovano i dati e decidere come deserializzare. Questa tecnica di versioning è utile se si memorizzano dati serializzati che sopravvivono a diverse versioni del codice.

Ma la memorizzazione di dati serializzati per così tanto tempo non è molto comune. È molto più comune utilizzare il meccanismo di serializzazione per scrivere temporaneamente dati ad esempio in una cache o inviarli in rete a un altro programma con la stessa versione delle parti pertinenti della base di codice.

In questo caso non sei interessato a mantenere la compatibilità con le versioni precedenti. Ti preoccupi solo di assicurarti che le basi di codice che comunicano abbiano effettivamente le stesse versioni delle classi pertinenti. Al fine di facilitare tale controllo, è necessario mantenere il serialVersionUIDproprio come prima e non dimenticare di aggiornarlo quando si apportano modifiche alle classi.

Se dimentichi di aggiornare il campo, potresti finire con due diverse versioni di una classe con struttura diversa ma con la stessa serialVersionUID. In tal caso, il meccanismo predefinito ( in.defaultReadObject()) non rileverà alcuna differenza e proverà a deserializzare i dati incompatibili. Ora potresti finire con un errore di runtime criptico o un errore silenzioso (campi null). Questi tipi di errori potrebbero essere difficili da trovare.

Quindi, per aiutare questo caso d'uso, la piattaforma Java ti offre la possibilità di non impostare serialVersionUIDmanualmente. Invece, un hash della struttura di classe verrà generato in fase di compilazione e utilizzato come id. Questo meccanismo farà in modo che non si abbiano mai strutture di classe diverse con lo stesso ID e quindi non si otterranno questi errori di serializzazione di runtime difficili da rintracciare menzionati sopra.

Ma c'è un lato negativo nella strategia id generata automaticamente. Vale a dire che gli ID generati per la stessa classe potrebbero differire tra compilatori (come menzionato sopra Jon Skeet). Pertanto, se si comunicano dati serializzati tra codice compilato con compilatori diversi, si consiglia comunque di conservare gli ID manualmente.

E se sei retrocompatibile con i tuoi dati come nel primo caso d'uso menzionato, probabilmente vorrai anche mantenere l'ID da solo. Questo al fine di ottenere ID leggibili e avere un maggiore controllo su quando e come cambiano.


4
L'aggiunta o la rimozione di campi non transitori non rende la classe serializzazione incompatibile. Non vi è quindi alcun motivo per "sbatterlo" su tali cambiamenti.
Marchese di Lorne,

4
@EJP: Huh? L'aggiunta di dati modifica sicuramente i dati di serializzazione nel mio mondo.
Alexander Torstling,

3
@AlexanderTorstling Leggi cosa ho scritto. Non ho detto che non "modifica i dati di serializzazione". Ho detto che "non rende incompatibile la serializzazione di classe". Non è la stessa cosa È necessario leggere il capitolo Controllo delle versioni della specifica di serializzazione degli oggetti.
Marchese di Lorne,

3
@EJP: mi rendo conto che l'aggiunta di un campo non transitorio non significa necessariamente che la classe serializzazione sia incompatibile, ma si tratta di una modifica strutturale che modifica i dati serializzati e di solito si desidera eseguire il bump della versione quando lo si fa a meno che non si gestire la retrocompatibilità, che ho anche spiegato più avanti nel post. Qual è esattamente il tuo punto?
Alexander Torstling,

4
Il mio punto rimane esattamente quello che ho detto. L'aggiunta o la rimozione di campi non transitori non rende la classe serializzazione incompatibile. Pertanto non è necessario eseguire il bump di serialVersionUID ogni volta che lo si fa.
Marchese di Lorne,

72

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

SerialVersionUIDè un identificatore univoco per ogni classe, lo JVMutilizza per confrontare le versioni della classe assicurandosi che la stessa classe utilizzata durante la serializzazione sia caricata durante la deserializzazione.

Se si specifica uno si ottiene un maggiore controllo, sebbene JVM ne generi uno se non si specifica. Il valore generato può differire tra diversi compilatori. Inoltre, a volte vuoi solo per qualche motivo vietare la deserializzazione di vecchi oggetti serializzati [ backward incompatibility], e in questo caso devi solo cambiare serialVersionUID.

I javadocs perSerializable dire :

il calcolo serialVersionUID predefinito è altamente sensibile ai dettagli della classe che possono variare a seconda delle implementazioni del compilatore e può quindi provocare InvalidClassExceptions imprevisti durante la deserializzazione.

Pertanto, è necessario dichiarare serialVersionUID perché ci offre un maggiore controllo .

Questo articolo ha alcuni punti positivi sull'argomento.


3
@Vinothbabu ma serialVersionUID è statico, pertanto non è possibile serializzare le variabili statiche. allora come mai jvm controllerà la versione, senza sapere qual è la versione dell'oggetto deserializzante
Kranthi Sama,

3
L'unica cosa non menzionata in questa risposta è che potresti causare conseguenze indesiderate includendo ciecamente serialVersionUIDsenza sapere il perché. Il commento di Tom Anderson sulla risposta di MetroidFan2002 si rivolge a questo: "Direi che se non stai usando la serializzazione per l'archiviazione permanente, dovresti usare @SuppressWarnings anziché aggiungere un valore. Raggruppa la classe di meno e preserva l'abilità del meccanismo serialVersionUID per proteggerti da modifiche incompatibili. "
Kirby,

7
serialVersionUIDnon è un "identificatore univoco per ogni classe". Il nome di classe completo è quello. È un indicatore di versione .
Marchese di Lorne,

58

La domanda originale ha chiesto "perché è importante" ed "esempio" dove ciò Serial Version IDsarebbe utile. Bene, ne ho trovato uno.

Supponi di creare una Carclasse, di crearne un'istanza e di scriverla in un flusso di oggetti. L'oggetto auto appiattito si trova nel file system per qualche tempo. Nel frattempo, se la Carclasse viene modificata aggiungendo un nuovo campo. Più tardi, quando si tenta di leggere (cioè deserializzare) l' Caroggetto appiattito , si ottiene java.io.InvalidClassException- perché 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 pensate davvero, l'eccezione viene generata a causa dell'aggiunta del nuovo campo. Puoi evitare che questa eccezione venga generata controllando tu stesso il controllo delle versioni dichiarando un serialVersionUID esplicito. C'è anche un piccolo vantaggio in termini di prestazioni nel dichiarare esplicitamente il tuoserialVersionUID(perché non deve essere calcolato). Pertanto, è consigliabile aggiungere il proprio serialVersionUID alle classi serializzabili non appena le si crea come mostrato di seguito:

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

E si dovrebbe assegnare un numero lungo casuale, non 1L, a ogni UID.
abbas

4
@abbas 'Uno dovrebbe' farlo perché? Spiega che differenza fa.
Marchese di Lorne,

Come dice il nome, rappresenta la versione della classe utilizzata per serializzare l'oggetto. Se lo assegni sempre lo stesso numero, non sarai in grado di trovare la classe giusta per la serializzazione. Quindi, ogni volta che fai una modifica nella classe, è meglio cambiare anche la versione.
abbas,

2
@abbas, questa intenzione non si scontra con l'utilizzo di numeri naturali incrementali da 1e così via.
Vadzim,

2
@BillK, ho pensato che il controllo di serializzazione fosse associato alla coppia di classname e serialVersionUID. Quindi schemi di numerazione diversi di classi e librerie diverse non possono interferire in alcun modo. O hai implicato librerie che generano codice?
Vadzim,

44

Per prima cosa devo spiegare cos'è la serializzazione.

La serializzazione consente di convertire un oggetto in un flusso, per l'invio di tale oggetto sulla rete O Salva su file O salva in DB per l'uso della lettera.

Esistono alcune regole per la serializzazione .

  • Un oggetto è serializzabile solo se la sua classe o la sua superclasse implementa l'interfaccia Serializable

  • Un oggetto è serializzabile (implementa esso stesso l'interfaccia Serializable) anche se la sua superclasse non lo è. Tuttavia, la prima superclasse nella gerarchia della classe serializzabile, che non implementa l'interfaccia Serializable, DEVE avere un costruttore no-arg. Se questo viene violato, readObject () produrrà un java.io.InvalidClassException in runtime

  • Tutti i tipi primitivi sono serializzabili.

  • I campi transitori (con modificatore transitorio) NON sono serializzati (cioè non salvati o ripristinati). Una classe che implementa Serializable deve contrassegnare i campi transitori di classi che non supportano la serializzazione (ad esempio, un flusso di file).

  • I campi statici (con modificatore statico) non sono serializzati.

Quando Objectè serializzato, Java Runtime associa il numero di versione seriale aka, il serialVersionID.

Dove abbiamo bisogno di serialVersionID: durante la deserializzazione per verificare che il mittente e il destinatario siano compatibili rispetto alla serializzazione. Se il destinatario ha caricato la classe con una diversa, laserialVersionIDdeserializzazione termina conInvalidClassCastException.
Una classe serializzabile può dichiararsiserialVersionUIDesplicitamente dichiarando un campo denominatoserialVersionUIDche deve essere statico, finale e di tipo lungo.

Proviamo questo con un esempio.

import java.io.Serializable;    
public class Employee implements Serializable {
private static final long serialVersionUID = 1L;
private String empname;
private byte empage;

public String getEmpName() {
    return name;
}
public void setEmpName(String empname) {
    this.empname = empname;
}
public byte getEmpAge() {
    return empage;
}
public void setEmpAge(byte empage) {
    this.empage = empage;
}

public String whoIsThis() {
    StringBuffer employee = new StringBuffer();
    employee.append(getEmpName()).append(" is ).append(getEmpAge()).append("
years old  "));
    return employee.toString();
}
}

Crea oggetto serializza

import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectOutputStream;
public class Writer {
public static void main(String[] args) throws IOException {
    Employee employee = new Employee();
    employee.setEmpName("Jagdish");
    employee.setEmpAge((byte) 30);

    FileOutputStream fout = new 
FileOutputStream("/users/Jagdish.vala/employee.obj");
    ObjectOutputStream oos = new ObjectOutputStream(fout);
    oos.writeObject(employee);
    oos.close();
    System.out.println("Process complete");
}
}

Deserializza l'oggetto

import java.io.FileInputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
public class Reader {
public static void main(String[] args) throws ClassNotFoundException, 
IOException {
    Employee employee = new Employee();
    FileInputStream fin = new 
    FileInputStream("/users/Jagdish.vala/employee.obj");
    ObjectInputStream ois = new ObjectInputStream(fin);
    employee = (Employee) ois.readObject();
    ois.close();
    System.out.println(employee.whoIsThis());
 }
}    

NOTA: ora modificare serialVersionUID della classe Employee e salvare:

private static final long serialVersionUID = 4L;

Ed esegui la classe Reader. Non eseguire la classe Writer e otterrai l'eccezione.

Exception in thread "main" java.io.InvalidClassException: 
com.jagdish.vala.java.serialVersion.Employee; local class incompatible: 
stream classdesc serialVersionUID = 1, local class serialVersionUID = 4
at java.io.ObjectStreamClass.initNonProxy(ObjectStreamClass.java:616)
at java.io.ObjectInputStream.readNonProxyDesc(ObjectInputStream.java:1623)
at java.io.ObjectInputStream.readClassDesc(ObjectInputStream.java:1518)
at java.io.ObjectInputStream.readOrdinaryObject(ObjectInputStream.java:1774)
at java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1351)
at java.io.ObjectInputStream.readObject(ObjectInputStream.java:371)
at com.krishantha.sample.java.serialVersion.Reader.main(Reader.java:14)

Correggimi se sbaglio: la classe locale è quella che stai attualmente / utilizzando nel percorso di classe e il flusso è quello usato da un'altra parte (es. Un server che ti sta restituendo una risposta e ha serializzato il risposta). Puoi riscontrare questa situazione quando stai comunicando con un server che ha aggiornato le sue librerie di terze parti, ma tu (il client) non l'hai fatto.
Victor

37

Se non avrai mai bisogno di serializzare i tuoi oggetti su array di byte e inviarli / archiviarli, non devi preoccuparti di ciò. Se lo fai, allora devi considerare serialVersionUID poiché il deserializzatore dell'oggetto lo abbinerà alla versione dell'oggetto che ha il suo classloader. Maggiori informazioni al riguardo nelle specifiche del linguaggio Java.


10
Se non si intende serializzare gli oggetti, perché sono serializzabili?
Erickson,

7
@erickson: la classe genitore può essere serializzabile, ad esempio ArrayList, ma si desidera che il proprio oggetto (ad esempio un elenco di array modificato) lo utilizzi come base ma che non verrà mai serializzato la raccolta creata.
MetroidFan2002,

5
Non è menzionato da nessuna parte nelle specifiche del linguaggio Java. È menzionato nelle specifiche di versione degli oggetti.
Marchese di Lorne,

4
Ecco il link alla specifica di versione degli oggetti Java 8 .
Basil Bourque,

34

Se ricevi questo avviso su una classe, non pensi mai alla serializzazione e non ti dichiari implements Serializable , spesso è perché hai ereditato da una superclasse, che implementa Serializable. Spesso sarebbe meglio delegare a tale oggetto invece di usare l'ereditarietà.

Quindi, invece di

public class MyExample extends ArrayList<String> {

    public MyExample() {
        super();
    }
    ...
}

fare

public class MyExample {
    private List<String> myList;

    public MyExample() {
         this.myList = new ArrayList<String>();
    }
    ...
}

e nei metodi pertinenti chiama myList.foo()invece di this.foo()(o super.foo()). (Questo non si adatta in tutti i casi, ma ancora abbastanza spesso.)

Vedo spesso persone che estendono JFrame o simili, quando in realtà hanno solo bisogno di delegare a questo. (Questo aiuta anche per il completamento automatico in un IDE, poiché JFrame ha centinaia di metodi, che non ti servono quando vuoi chiamare quelli personalizzati nella tua classe.)

Un caso in cui l'avviso (o serialVersionUID) è inevitabile è quando si estende da AbstractAction, normalmente in una classe anonima, aggiungendo solo il metodo actionPerformed. Penso che non ci dovrebbe essere un avvertimento in questo caso (dal momento che normalmente non è possibile serializzare in modo affidabile e deserializzare tali classi anonime comunque attraverso diverse versioni della classe), ma non sono sicuro di come il compilatore possa riconoscerlo.


4
Penso che tu abbia ragione che la composizione sull'inerzia ha più senso, in particolare quando stai discutendo di lezioni come ArrayList. Tuttavia, molti framework richiedono che le persone si estendano da superclassi astratte che sono serializzabili (come la classe ActionForm di Struts 1.2 o la ExtensionFunctionDefinition di Saxon) nel qual caso questa soluzione è impossibile. Penso che tu abbia ragione, sarebbe bello se l'avvertimento fosse ignorato in alcuni casi (come se ti stessi estendendo da una classe serializzabile astratta)
Piepera

2
Sicuramente se aggiungi una classe come membro, anziché ereditarla, dovresti scrivere un metodo wrapper per OGNI metodo della classe membro che desideri utilizzare, il che lo renderebbe irrealizzabile in un gran numero di situazioni .. a meno che java abbia una funzione simile a quella di perl __AUTOLOAD, di cui non so nulla.
M_M,

4
@M_M: quando delegheresti molti metodi al tuo oggetto spostato, ovviamente non sarebbe appropriato usare la delega. Ma suppongo che questo caso sia un segno di un errore di progettazione - gli utenti della tua classe (ad esempio "MainGui") non dovrebbero aver bisogno di chiamare molti metodi dell'oggetto avvolto (ad esempio JFrame).
Paŭlo Ebermann,

1
Quello che non mi piace della delega è la necessità di avere un riferimento al delegato. E ogni riferimento significa più memoria. Correggimi se sbaglio. Se avessi bisogno di un CustomizedArrayList di 100 oggetti, questo non avrebbe molta importanza, ma se avessi bisogno di centinaia di CustomizdeArrayLists di alcuni oggetti, l'utilizzo della memoria aumenta in modo significativo.
WVrock,

2
Throwable è serializzabile e solo Throwable è gettabile, quindi non è possibile definire un'eccezione che non sia serializzabile. La delega non è possibile.
Phil

22

Per comprendere il significato del campo serialVersionUID, è necessario capire come funziona la serializzazione / deserializzazione.

Quando un oggetto di classe serializzabile viene serializzato Java Runtime associa un numero di versione seriale (chiamato serialVersionUID) a questo oggetto serializzato. Nel momento in cui si deserializza questo oggetto serializzato Java Runtime corrisponde al serialVersionUID dell'oggetto serializzato con il serialVersionUID della classe. Se entrambi sono uguali, procede solo con l'ulteriore processo di deserializzazione, altrimenti genera 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 verrà associato all'oggetto serializzato e alla classe, indipendentemente dalla piattaforma di serializzazione e deserializzazione (ad es. La serializzazione potrebbe essere eseguita su piattaforma come Windows usando sun o MS JVM e Deserialization potrebbero trovarsi su piattaforme Linux diverse usando Zing JVM).

Ma nel caso in cui serialVersionUID non sia specificato dal programmatore, durante la serializzazione \ la serializzazione di qualsiasi oggetto, il runtime Java utilizza il proprio algoritmo per calcolarlo. Questo algoritmo di calcolo serialVersionUID varia da un JRE all'altro. È anche possibile che l'ambiente in cui l'oggetto è serializzato stia usando un JRE (es: SUN JVM) e che l'ambiente in cui si verifica la deserializzazione stia usando Linux Jvm (zing). In tali casi serialVersionUID associato all'oggetto serializzato sarà diverso dal serialVersionUID della classe calcolata nell'ambiente di deserializzazione. A sua volta, la deserializzazione non avrà successo. Quindi, per evitare tali situazioni / problemi, il programmatore deve sempre specificare serialVersionUID della classe serializzabile.


2
L'algoritmo non varia, ma è leggermente sotto-specificato.
Marchese di Lorne,

... l'algoritmo non varia, ma è leggermente sotto-specificato ... cioè la JVM può variare ..... @ user207421
AnthonyJClink

18

Non preoccuparti, il calcolo predefinito è davvero buono e sufficiente per il 99,9999% dei casi. E se si verificano problemi, è possibile - come già affermato - introdurre l'UID come necessità necessaria (che è altamente improbabile)


2
Sciocchezze. È adeguato nel caso in cui la classe non sia cambiata. Hai prove zero per supportare '99 .9999% '.
Marchese di Lorne,

18

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

Sto lavorando su questa applicazione Java EE che è composta da un modulo Web che utilizza un EJBmodulo. Il modulo Web chiama il EJBmodulo in remoto e passa un POJOche implementaSerializable un argomento come argomento.

Questo POJO's classe è stata impacchettata all'interno del vaso EJB e all'interno del proprio vaso nel WEB-INF / lib del modulo web. Sono in realtà la stessa classe, ma quando impacco il modulo EJB scompatto il vaso di questo POJO per impacchettarlo insieme al modulo EJB.

La chiamata al EJBfalliva 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

16

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

Lo saprei quando lo uso ObjectInputStreame ObjectOutputStreamper la mia applicazione o se conosco una libreria / framework che userò lo userò. SerialVersionID garantisce che diverse VM Java di diverse versioni o fornitori interagiscano correttamente o, ad esempio, se vengono archiviate e recuperate al di fuori della VMHttpSession i dati della sessione possono rimanere anche durante il riavvio e l'aggiornamento del server delle applicazioni.

Per tutti gli altri casi, io uso

@SuppressWarnings("serial")

poiché il più delle volte il valore predefinito serialVersionUIDè sufficiente. Questo include Exception, HttpServlet.


Non include HttpServlet in contenitori in cui possono essere scambiati o, ad esempio, Eccezione in RMI.
Marchese di Lorne,

14

I dati del campo rappresentano alcune informazioni memorizzate nella classe. Class implementa l' Serializableinterfaccia, quindi eclipse si offre automaticamente di dichiarare il serialVersionUIDcampo. Iniziamo con il valore 1 impostato lì.

Se non vuoi che arrivi questo avviso, usa questo:

@SuppressWarnings("serial")

11

Sarebbe bello se CheckStyle potesse verificare che 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 hai un progetto con molti DTO serializzabili, ad esempio, ricordarti di eliminare il serialVersionUID esistente e rigenerarlo è una seccatura, e attualmente l'unico modo (che conosco) di verificare questo è rigenerare per ogni classe e confrontarlo con quello vecchio. Questo è molto doloroso.


8
Se imposti serialVersionUID sempre sullo stesso valore che il generatore produrrebbe, non ne hai davvero bisogno. Dopotutto, la sua ragion d'essere è rimanere lo stesso dopo i cambiamenti, quando la classe è ancora compatibile.
Paŭlo Ebermann,

4
Il motivo è che diversi compilatori hanno lo stesso valore per la stessa classe. Come spiegato nei javadocs (anche la risposta sopra), la versione generata è fragile e può variare anche quando la classe è propriamente deserializzabile. Finché si esegue questo test sullo stesso compilatore ogni volta, dovrebbe essere sicuro. dio ti aiuti se aggiorni jdk e esce una nuova regola, anche se il tuo codice non è cambiato.
Andrew Backer,

2
Non è necessario per abbinare ciò serialverche produrrebbe. -1
Marchese di Lorne,

In generale non è richiesto affatto. Il caso di @ AndrewBacker richiede due diversi compilatori sullo stesso file .java con entrambe le versioni dei file .class che comunicano tra loro - il più delle volte basta creare la classe e distribuirla. In tal caso, non avere un SUID funzionerebbe bene.
Bill K,

1
Le persone che usano veramente la serializzazione per scopi di archiviazione / recupero impostano generalmente serialVersionUIDsu 1. Se una versione più recente della classe è incompatibile, ma richiede comunque di essere in grado di gestire i vecchi dati, si aumenta il numero di versione e si aggiunge il codice speciale a gestire formati precedenti. Piango ogni volta che vedo una serialVersionUIDcifra maggiore di 1 cifra, sia perché era un numero casuale (inutile) o perché la classe apparentemente ha bisogno di gestire più di 10 versioni diverse.
john16384,

11

SerialVersionUID viene utilizzato per il controllo della versione dell'oggetto. puoi specificare serialVersionUID anche nel tuo file di classe. La conseguenza di non specificare serialVersionUID è che quando si aggiunge o si modifica un campo in una classe, la classe già serializzata non sarà in grado di recuperare poiché 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 recupero dello stato dell'oggetto serializzato e genera java.io.InvalidClassException in caso di mancata corrispondenza serialVersionUID

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


9

Perché usare la classe SerialVersionUIDinterna Serializablein Java?

Durante il serializationruntime Java viene creato un numero di versione per una classe, in modo che possa serializzarlo in un secondo momento. Questo numero di versione è noto come SerialVersionUIDin Java.

SerialVersionUIDviene utilizzato per la versione dei dati serializzati. È possibile annullare la serializzazione di una classe solo se SerialVersionUIDcorrisponde all'istanza serializzata. Quando non dichiariamo SerialVersionUIDnella nostra classe, il runtime Java 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, il runtime Java persiste l'istanza di quella classe sul disco utilizzando il meccanismo di serializzazione predefinito, a condizione che non si sia personalizzato il processo mediante l' Externalizableinterfaccia.

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


8

Se vuoi modificare un numero enorme di classi che non hanno impostato serialVersionUID in primo luogo mantenendo la compatibilità con le vecchie classi, strumenti come IntelliJ Idea, Eclipse non riescono poiché generano numeri casuali e non funzionano su un mucchio di file in un colpo solo. Mi viene in mente il seguente script bash (mi dispiace per gli utenti Windows, considera l'acquisto di un Mac o la conversione in Linux) per apportare facilmente modifiche al 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

salvi questo script, dì add_serialVersionUID.sh a te ~ / 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 JVK serialVer sotto il cofano. Quindi assicurati che $ JAVA_HOME / bin sia nel PERCORSO.


2
Mi dà un'idea: rigenerare sempre la versione seriale uid con uno strumento come questo, mai a mano, prima di rilasciare una versione - in questo modo, si evita di dimenticare di apportare una modifica a una classe la cui versione seriale uid avrebbe dovuto cambiare a causa di un effettivo cambiamento incompatibile. Tenere traccia di ciò manualmente è molto difficile.
Ignazio

6

Questa domanda è documentata molto bene in Effective Java da Joshua Bloch. Un ottimo libro e assolutamente da leggere. Descriverò alcuni dei motivi seguenti:

Il runtime di serializzazione presenta un numero chiamato Versione seriale per ogni classe serializzabile. Questo numero si chiama serialVersionUID. Ora c'è un po 'di matematica dietro questo numero e viene fuori in base ai campi / metodi definiti nella classe. Per la stessa classe viene sempre generata la stessa versione. Questo numero viene utilizzato durante la deserializzazione per verificare che il mittente e il destinatario di un oggetto serializzato abbiano caricato classi per quell'oggetto compatibili rispetto alla 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 si tradurrà in InvalidClassException.

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


6

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

Prendi in considerazione una situazione: in futuro dovrai modificare o cambiare la tua classe e in quel 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 (oggetto dipendente) serializzato (n. 333) con quello della classe, ad esempio n. 444 (da quando è stato modificato). A confronto JVM troverà entrambe le versioni UID diverse e quindi la deserializzazione fallirà. Quindi se serialVersionID per ogni classe è definito dal programmatore stesso. Sarà lo stesso anche se la classe si evolverà in futuro e quindi JVM troverà sempre che la classe è compatibile con l'oggetto serializzato anche se la classe è cambiata. Per maggiori informazioni puoi consultare il capitolo 14 di HEAD FIRST JAVA.


1
Ogni volta che la classe di un oggetto viene serializzata serialVersionUIDviene trasmessa. Non viene inviato con ogni oggetto.
Marchese di Lorne,

3

Una semplice spiegazione:

  1. Stai serializzando i dati?

    La serializzazione fondamentalmente sta scrivendo i dati di classe su un file / stream / etc. La deserializzazione sta leggendo i dati in una classe.

  2. Hai intenzione di andare in produzione?

    Se stai testando qualcosa con dati non importanti / falsi, non preoccuparti (a meno che non stia testando direttamente la serializzazione).

  3. Questa è la prima versione?

    In tal caso, impostare serialVersionUID=1L.

  4. Questa è la seconda, terza, ecc. Versione prod?

    Ora devi preoccuparti serialVersionUIDe approfondire.

Fondamentalmente, se non aggiorni la versione correttamente quando aggiorni una classe che devi scrivere / leggere, otterrai un errore quando provi a leggere i vecchi dati.


2

'serialVersionUID' è un numero a 64 bit utilizzato per identificare in modo univoco una classe durante il processo di deserializzazione. Quando si serializza un oggetto, anche serialVersionUID della classe viene scritto nel file. Ogni volta che si deserializza questo oggetto, il tempo di esecuzione java estrae questo valore serialVersionUID dai dati serializzati e confronta lo stesso valore associato alla classe. Se entrambi non corrispondono, verrà generato "java.io.InvalidClassException".

Se una classe serializzabile non dichiara esplicitamente un serialVersionUID, il runtime di serializzazione calcolerà il valore serialVersionUID per quella classe in base a vari aspetti della classe come campi, metodi ecc., È possibile fare riferimento a questo collegamento per l'applicazione demo.


1

Per farla breve, questo campo viene utilizzato per verificare se i dati serializzati possono essere deserializzati correttamente. La serializzazione e la deserializzazione vengono spesso eseguite da diverse copie del programma, ad esempio il server converte l'oggetto in stringa e il client converte la stringa ricevuta in oggetto. Questo campo indica che entrambi operano con la stessa idea di cosa sia questo oggetto. Questo campo aiuta quando:

  • hai molte copie diverse del tuo programma in luoghi diversi (come 1 server e 100 client). Se cambierai il tuo oggetto, modificherai il tuo numero di versione e dimenticherai di aggiornare uno di questi client, saprà che non è in grado di deserializzare

  • hai archiviato i tuoi dati in alcuni file e in seguito provi ad aprirli con la versione aggiornata del tuo programma con oggetto modificato - saprai che questo file non è compatibile se mantieni la tua versione corretta

Quando è importante?

Più ovvio: se aggiungi alcuni campi al tuo oggetto, le versioni precedenti non saranno in grado di usarli perché non hanno questi campi nella loro struttura ad oggetti.

Meno ovvio: quando si deserializza l'oggetto, i campi che non erano presenti nella stringa verranno mantenuti come NULL. Se hai rimosso il campo dal tuo oggetto, le versioni precedenti manterranno questo campo come sempre-NULL che può portare a comportamenti errati se le versioni precedenti si basano sui dati in questo campo (comunque lo hai creato per qualcosa, non solo per divertimento :-))

Il meno ovvio: a volte si cambia l'idea che si inserisce nel significato di alcuni campi. Ad esempio, quando hai 12 anni intendi "bicicletta" sotto "bici", ma quando hai 18 anni intendi "motocicletta" - se i tuoi amici ti inviteranno a "andare in bicicletta attraverso la città" e sarai l'unico che è venuto in bicicletta, capirai quanto sia importante mantenere lo stesso significato in tutti i campi :-)


1

In primo luogo per rispondere alla tua domanda, quando non dichiariamo SerialVersionUID nella nostra classe, il runtime Java lo genera per noi, ma quel processo è sensibile a molti metadati di classe tra cui numero di campi, tipo di campi, modificatore di accesso dei campi, interfaccia implementata per classe ecc. Pertanto, si consiglia di dichiararlo noi stessi ed Eclipse ti avverte dello stesso.

Serializzazione: spesso lavoriamo con oggetti importanti il ​​cui stato (dati nelle variabili dell'oggetto) è così importante che non possiamo rischiare di perderli a causa di interruzioni dell'alimentazione / del sistema (o) in caso di invio dello stato dell'oggetto ad altri macchina. La soluzione per questo problema si chiama "Persistenza" che significa semplicemente persistere (conservare / salvare) i dati. La serializzazione è uno dei molti altri modi per raggiungere la persistenza (salvando i dati su disco / memoria). Quando si salva lo stato dell'oggetto, è importante creare un'identità per l'oggetto, per poterlo rileggere correttamente (deserializzazione). Questa identificazione univoca è ID è SerialVersionUID.


0

Che cos'è SerialVersionUID? Risposta: - Diciamo che ci sono due persone, una da HQ e un'altra da ODC, entrambe eseguiranno rispettivamente la serializzazione e la deserializzazione. In questo caso per autenticare che il destinatario che si trova in ODC è la persona autenticata, JVM crea un ID univoco noto come SerialVersionUID.

Ecco una bella spiegazione basata sullo scenario,

Perché SerialVersionUID?

Serializzazione : al momento della serializzazione, con ogni lato del mittente dell'oggetto JVM salverà un identificatore univoco. JVM è responsabile di generare quell'ID univoco basato sul file .class corrispondente presente nel sistema del mittente.

Deserializzazione : al momento della deserializzazione, JVM lato destinatario confronta l'ID univoco associato all'oggetto con ID univoco di classe locale, ovvero JVM creerà anche un ID univoco basato sul file .class corrispondente presente nel sistema di ricezione. Se entrambi gli ID univoci corrispondono, verrà eseguita solo la deserializzazione. Altrimenti otterremo un'eccezione di runtime che dice InvalidClassException. Questo identificatore univoco non è altro che SerialVersionUID

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.