Perché generare un serialVersionUID lungo anziché un semplice 1L?


210

Quando la classe implementa Serializable in Eclipse, ho due opzioni: aggiungi default serialVersionUID(1L)o generato serialVersionUID(3567653491060394677L). Penso che il primo sia più bello, ma molte volte ho visto le persone usare la seconda opzione. C'è qualche motivo per generare long serialVersionUID?


49
Come è esattamente il duplicato? Non chiedo perché generarlo affatto, ma perché generare un serialVersionUID lungo.
IAdapter,

13
Quando Jon Skeet usa serialVersionUID, usa 0L: stackoverflow.com/questions/605828/… ;)
Hanno Fietz,

8
@HannoFietz: La frase esatta è: "Per semplicità suggerirei di iniziare da 0 e di aumentarlo di 1 ogni volta che è necessario." Quindi, sembra che usi 0Lsolo inizialmente.
OR Mapper

7
@ORMapper: Stai insinuando che Jon Skeet deve mai tornare indietro e aggiornare il codice che ha scritto? Anche al punto di incompatibilità strutturale. Gasp! Eresia!
Thilo,

Risposte:


90

Per quanto ne so, sarebbe solo per compatibilità con le versioni precedenti. Ciò sarebbe utile solo se si è trascurato di utilizzare un serialVersionUID in precedenza e quindi si è apportato una modifica che si sa che dovrebbe essere compatibile ma che causa l'interruzione della serializzazione.

Vedere le specifiche di serializzazione Java per maggiori dettagli.


69

Lo scopo dell'UID della versione di serializzazione è di tenere traccia delle diverse versioni di una classe al fine di eseguire una serializzazione valida degli oggetti.

L'idea è quella di generare un ID univoco per una determinata versione di una classe, che viene quindi modificato quando vengono aggiunti nuovi dettagli alla classe, come un nuovo campo, che influirebbe sulla struttura dell'oggetto serializzato.

Usando sempre lo stesso ID, ad esempio 1L, in futuro, se viene modificata la definizione della classe che causa cambiamenti nella struttura dell'oggetto serializzato, ci saranno buone probabilità che sorgano problemi quando si cerca di deserializzare un oggetto.

Se l'ID viene omesso, Java calcolerà l'ID per te in base ai campi dell'oggetto, ma credo che sia un processo costoso, quindi fornirne uno manualmente migliorerà le prestazioni.

Ecco un paio di collegamenti ad articoli che parlano della serializzazione e del versioning delle classi:


50
L'idea alla base dell'utilizzo di 1L è quella di incrementarla ogni volta che si modificano le proprietà oi metodi della classe.
Powerlord,

39
Non vi è alcun impatto sulle prestazioni di runtime nel consentire la generazione automatica del serialversionUID: viene generato in fase di compilazione da javac ... se decompilate il bytecode della classe, vedrete effettivamente la variabile staticamente nel bytecode.
Jared,

11
Ancora una nota: gestendo esplicitamente il numero, puoi decidere quando consideri le versioni di una classe "compatibili", anziché richiedere che la definizione della classe sia esattamente la stessa.
Scott Stanchfield,

23
@ Jared Secondo l'articolo 75 di Effective Java di Josh Bloch: 2a edizione: "dichiarare un UID di versione seriale esplicito in ogni classe serializzabile che si scrive .... Se non viene fornito alcun UID di versione seriale, è necessario un calcolo costoso per generarne uno in fase di runtime ".
Colin K,

12
@coobird Questo sembra essere il motivo principale per cui il serialVersionUID predefinito non è raccomandato Note - It is strongly recommended that all serializable classes explicitly declare serialVersionUID values, since the default serialVersionUID computation is highly sensitive to class details that may vary depending on compiler implementations, and can thus result in unexpected serialVersionUID conflicts during deserialization, causing deserialization to fail. Il commento sopra è stato preso dalla specifica di serializzazione degli oggetti Java versione 6.0
user624558

20

Il motivo principale per quello generato sarebbe quello di renderlo compatibile con una versione esistente della classe che ha già copie persistenti.


1
OK, ma lo stesso sarà se ho sempre 1L. Tutto sarà compatibile anche se farò qualche modifica.
grep,

@grep prova a rinominare un campo e poi vedi cosa succede.
Trejkaz,

1
@grep il punto è che se hai una classe che ha omesso serialVersionUID in precedenza, otterrebbe automaticamente quella generata. Quindi ora, vuoi iniziare a impostarlo esplicitamente, impostandolo su 1L lo renderebbe incompatibile con la classe esistente, mentre l'uso del valore generato lo mantiene compatibile.
marc82ch,

15

Il valore predefinito "lungo" di serialVersionUIDè il valore predefinito definito dalla specifica di serializzazione Java , calcolato dal comportamento di serializzazione predefinito.

Quindi, se aggiungi il numero di versione predefinito, la tua classe (de-) serializzerà più velocemente fino a quando nulla è cambiato strutturalmente, ma dovrai fare attenzione che se cambi la classe (aggiungi / rimuovi campi) aggiorni anche il numero di serie.

Se non devi essere compatibile con i flussi di bit esistenti, puoi semplicemente inserirlo 1Le incrementare la versione in base alle necessità quando qualcosa cambia. Cioè, quando la versione di serializzazione predefinita della classe modificata sarebbe diversa dalla versione predefinita della vecchia classe.


11

Dovresti assolutamente creare un serialVersionUID ogni volta che definisci una classe che implementa java.io.Serializable. In caso contrario, ne verrà creato uno automaticamente, ma non va bene. SerialVersionUID generato automaticamente si basa sulle firme del metodo della tua classe, quindi se in futuro cambi la classe per aggiungere un metodo (ad esempio), la deserializzazione delle versioni "vecchie" della classe fallirà. Ecco cosa può succedere:

  1. Crea la prima versione della tua classe, senza definire serialVersionUID.
  2. Serializza un'istanza della tua classe in un negozio persistente; un serialVersionUID viene generato automaticamente per te.
  3. Modifica la tua classe per aggiungere un nuovo metodo e ridistribuire l'applicazione.
  4. Tentativo di deserializzare l'istanza serializzata nel passaggio 2, ma ora non riesce (quando dovrebbe avere esito positivo) perché ha un serialVersionUID generato automaticamente diverso.

1
È un dato di fatto, la deserializzazione delle vecchie versioni della classe dovrebbe davvero fallire perché non sono più le stesse. Si suggerisce di generare serialVersionUID da soli per evitare errori di (de) serializzazione quando cambia la firma della classe. Sebbene il tuo suggerimento sia appropriato, la tua spiegazione del suo scopo è semplicemente sbagliata e fuorviante. Sarebbe saggio modificare la tua risposta.
mostruash,

6

Se non si specifica un serialVersionUID, Java ne crea uno al volo. Il serialVersionUID generato è quel numero. Se cambi qualcosa nella tua classe che in realtà non rende la tua classe incompatibile con le precedenti versioni serializzate ma cambia l'hash, allora devi usare il serialVersionUID di numero molto grande generato (o il numero "previsto" dal messaggio di errore) . Altrimenti, se stai tenendo traccia di tutto da solo, 0, 1, 2 ... è meglio.


Volevi dire ==> 1. Se vuoi che diversi cambi di classe siano compatibili usa quello generato. 2. Se si desidera che versioni diverse delle classi siano incompatibili, utilizzare quella predefinita ed essere prudenti nell'incremento. L'ho capito bene?
JavaDeveloper

4

Quando usi serialVersionUID (1L) anziché generare serialVersionUID (3567653491060394677L) stai dicendo qualcosa.

Stai dicendo che sei sicuro al 100% che nessun sistema toccherà mai questa classe che ha una versione serializzata incompatibile di questa classe con un numero di versione 1.

Se riesci a pensare a qualche scusa perché la sua cronologia delle versioni serializzata fosse sconosciuta, potrebbe essere difficile dirlo con sicurezza. Durante la sua vita, una classe di successo sarà gestita da molte persone, vivrà in molti progetti e risiederà in molti sistemi.

Puoi soffrire per questo. Oppure puoi giocare alla lotteria sperando di perdere. Se generi la versione hai una piccola possibilità che qualcosa vada storto. Se supponi "Ehi, scommetto che nessuno ha ancora usato 1" le tue probabilità sono più grandi che minuscole. È proprio perché tutti pensiamo che 0 e 1 siano belli che hai maggiori probabilità di colpirli.

-

Quando generi serialVersionUID (3567653491060394677L) anziché utilizzare serialVersionUID (1L) stai dicendo qualcosa.

Stai dicendo che le persone potrebbero aver creato o generato manualmente altri numeri di versione nella storia di questa classe e non ti interessa perché Longs sta dando di matto grandi numeri.

In entrambi i casi, a meno che tu non conosca perfettamente la storia dei numeri di versione utilizzati durante la serializzazione della classe nell'intero universo di dove è o sarà mai esistita, stai correndo una possibilità. Se hai il tempo di assicurarti che il 100% sia 1 AOK, provalo. Se questo richiede molto lavoro, vai avanti e genera il numero alla cieca. Hai più probabilità di vincere alla lotteria che di sbagliare. Se lo fa, fammi sapere e ti comprerò una birra.

Con tutto questo parlare di giocare alla lotteria potrei averti dato l'impressione che serialVersionUID sia generato casualmente. In effetti, fintanto che l'intervallo di numeri è uniformemente distribuito su ogni possibile valore di un Long, ciò andrebbe bene. Tuttavia, in realtà è fatto in questo modo:

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

L'unica differenza che ottieni è che non hai bisogno di una fonte di casualità. Stai usando le modifiche nella stessa classe per cambiare il risultato. Ma secondo il principio del piccione c'è ancora una possibilità che possa andare storto e avere una collisione. È semplicemente incredibilmente improbabile. Buona fortuna a farmi una birra.

Tuttavia, anche se la classe vivrà in un solo sistema e in una base di codice, pensare che incrementare il numero a mano non dia alcuna possibilità di collisioni significa solo che non capisci gli umani. :)


Se un "sistema" tocca la classe, cioè cambia la classe in modo tale che la serializzazione diventi incompatibile, allora la domanda è se quel sistema cambierà anche serialVersionUID. Non penso che le probabilità siano minori che ricorderà di cambiarlo quando sarà lungo. Penso che sia vero il contrario, se il numero è più facile da ricordare i cambiamenti sono più alti che noto che non l'ho modificato per caso.
Reto Gmür,

2
Questo è falso! Quando generi serialVersionUID e dichiari quel valore nel tuo codice sorgente, piuttosto che 1L o niente, in realtà stai dicendo: Voglio una collisione forse non rilevata in futuro con effetti indefiniti, e non voglio che java o altri umani lo impediscano . Java è paranoico, ma obbediente. Gli umani di solito non scherzano con grandi numeri. In questo modo, quando la classe cambia, java può ancora deserializzare vecchie versioni incompatibili di essa. MwoaHaHa ...;)
Superole il

1

Bene, serialVersionUID è un'eccezione alla regola secondo cui "i campi statici non vengono serializzati". ObjectOutputStream scrive ogni volta il valore di serialVersionUID nel flusso di output. ObjectInputStream lo legge di nuovo e se il valore letto dallo stream non coincide con il valore serialVersionUID nella versione corrente della classe, quindi genera InvalidClassException. Inoltre, se non vi è serialVersionUID dichiarato ufficialmente nella classe come serializzato, il compilatore lo aggiunge automaticamente con un valore generato in base ai campi dichiarati nella classe.


0

Perché in molti casi l'id predefinito non è univoco. quindi creiamo un ID per creare un concetto unico.


Puoi modificare la tua risposta per approfondire di più? Questo sembra un commento qui. Grazie.
Gray

0

Per aggiungere alla risposta di @David Schmitts, come regola empirica userei sempre 1L di default fuori convenzione. Ho dovuto solo tornare indietro e cambiarne alcune alcune volte, ma lo sapevo quando ho apportato la modifica e aggiornato il numero predefinito di uno ogni volta.

Nella mia azienda attuale richiedono il numero generato automaticamente, quindi lo uso per convenzione, ma preferisco il valore predefinito. La mia opinione è che, se non è una convenzione in cui lavori, usa l'impostazione predefinita, a meno che tu non pensi che cambierai costantemente la struttura delle tue classi serializzate per qualche motivo.


0

Lo scopo dell'UID della versione di serializzazione è di tenere traccia delle diverse versioni di una classe al fine di eseguire una serializzazione valida degli oggetti.

L'idea è quella di generare un ID univoco per una determinata versione di una classe, che viene quindi modificato quando vengono aggiunti nuovi dettagli alla classe, come un nuovo campo, che influirebbe sulla struttura dell'oggetto serializzato.

Una semplice spiegazione:

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.

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).

Questa è la prima versione?

In tal caso, impostare serialVersionUID = 1L.

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

Ora devi preoccuparti di serialVersionUID e approfondirlo.

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.

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.