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

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

Per quanto posso dire, sarebbe solo per la compatibilità con le versioni precedenti. Questo sarebbe utile solo se prima non avessi usato un serialVersionUID, e poi avresti apportato una modifica che sai essere compatibile, ma che causa la rottura della serializzazione.

Vedi le specifiche di serializzazione Java per maggiori dettagli.

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

L’idea è di generare un ID che sia univoco per una determinata versione di una class, che viene poi modificato quando vi sono nuovi dettagli aggiunti alla class, come un nuovo campo, che influenzerebbe la struttura dell’object serializzato.

Utilizzare sempre lo stesso ID, ad esempio 1L significa che in futuro, se viene modificata la definizione della class che causa modifiche alla struttura dell’object serializzato, ci saranno buone probabilità che si verifichino problemi quando si tenta di deserializzare un object.

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

Ecco alcuni link ad articoli che trattano la serializzazione e il controllo delle versioni delle classi:

  • Suggerimenti tecnici JDC: 29 febbraio 2000 (collegamento interrotto a febbraio 2013)
  • Scopri i segreti dell’API Serialization Java

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

Il valore predefinito “long” 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 class (de-) serializzerà più velocemente purché nulla sia stato modificato strutturalmente, ma dovrai fare attenzione che se cambi la class (aggiungi / rimuovi campi) aggiorni anche il numero di serie.

Se non è necessario essere compatibili con i flussi di bit esistenti, è sufficiente inserire 1L lì e incrementare la versione in base alle esigenze quando qualcosa cambia. Cioè, quando la versione di serializzazione predefinita della class modificata sarebbe diversa dalla versione predefinita della vecchia class.

Dovresti assolutamente creare un serialVersionUID ogni volta che definisci una class che implementa java.io.Serializable . Se non lo fai, verrà creato automaticamente per te, ma questo è male. Il serialVersionUID generato automaticamente si basa sulle firme del metodo della class, quindi se si modifica la class in futuro per aggiungere un metodo (ad esempio), deserializzare le versioni “vecchie” della class avrà esito negativo. Ecco cosa può accadere:

  1. Crea la prima versione della tua class, senza definire serialVersionUID.
  2. Serializzare un’istanza della class in un archivio persistente; un serialVersionUID viene generato automaticamente per te.
  3. Modifica la class per aggiungere un nuovo metodo e ridistribuire la tua applicazione.
  4. Tentativo di deserializzare l’istanza che è stata serializzata nel passaggio 2, ma ora non riesce (quando dovrebbe riuscire), perché ha un serialVersionUID diverso generato automaticamente.

Se non si specifica un serialVersionUID, Java ne crea uno al volo. Il serialVersionUID generato è quel numero. Se modifichi qualcosa nella tua class che non rende realmente la tua class incompatibile con i vereni serializzati precedenti ma modifica l’hash, allora devi usare il serialVersionUID (o il numero “previsto” generato dal numero di errore molto grande) dal messaggio di errore) . Altrimenti, se stai tenendo traccia di tutto da solo, 0, 1, 2 … è meglio.

Quando si utilizza serialVersionUID (1L) anziché generare serialVersionUID (3567653491060394677L) si sta dicendo qualcosa.

Stai dicendo che sei sicuro al 100% che nessun sistema che possa mai toccare questa class che ha una versione serializzata incompatibile di questa class con un numero di versione di 1.

Se riesci a trovare qualche scusa per la sua cronologia delle versioni serializzata da essere sconosciuta, potrebbe essere difficile dirlo con sicurezza. Nella sua vita, una class di successo sarà mantenuta da molte persone, vivrà in molti progetti e risiederà in molti sistemi.

Puoi agonizzare su questo. Oppure puoi giocare alla lotteria sperando di perdere. Se generi la versione hai una piccola possibilità che le cose vadano storte. Se si assume che “Ehi scommetto che nessuno ha usato ancora 1”, le tue probabilità sono più grandi di quelle minuscole. È proprio perché tutti noi pensiamo che 0 e 1 siano fantastici che tu abbia probabilità più alte di colpirli.

Quando si genera serialVersionUID (3567653491060394677L) anziché utilizzare serialVersionUID (1L) si sta dicendo qualcosa.

Stai dicendo che le persone potrebbero aver creato o generato manualmente altri numeri di versione nella cronologia di questa class e non ti interessa perché Longs sta facendo un gran numero di persone.

In entrambi i casi, a meno che tu non conosca perfettamente la cronologia dei numeri di versione usati quando serializzi la class nell’intero universo di dove è o sarà mai esistita, stai prendendo una possibilità. Se hai il tempo di rendere sicuro al 100% 1 è AOK, provaci. Se questo è troppo lavoro, vai avanti e genera ciecamente il numero. È più probabile che vinca la lotteria piuttosto che sbagliare. Se lo fa, fammelo 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, finché l’intervallo di numeri è uniformsmente distribuito su ogni valore ansible di 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 i cambiamenti nella class stessa per cambiare il risultato. Ma secondo il principio del Pigeonhole c’è ancora una possibilità che possa andare storto e avere una collisione. È incredibilmente improbabile. Quindi, buona fortuna, prendermi una birra.

Tuttavia, anche se la class vivrà sempre in un solo sistema e in una base di codice, pensando che incrementare il numero manualmente ti dà zero possibilità di collisioni significa semplicemente che non capisci gli umani. 🙂

Bene, serialVersionUID è un’eccezione alla regola secondo cui “i campi statici non vengono serializzati”. ObjectOutputStream scrive ogni volta il valore di serialVersionUID nel stream di output. ObjectInputStream lo legge di nuovo e se il valore letto dallo stream non concorda con il valore serialVersionUID nella versione corrente della class, genera quindi InvalidClassException. Inoltre, se non esiste serialVersionUID dichiarato ufficialmente nella class da serializzare, il compilatore lo aggiunge automaticamente con un valore generato in base ai campi dichiarati nella class.

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

Per aggiungere alla risposta di @David Schmitts, come regola generale utilizzerei sempre la convenzione 1L predefinita. 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 attuale compagnia richiedono il numero generato automaticamente, quindi lo uso per convenzione, ma preferisco l’impostazione predefinita. La mia opinione è che, se non si tratta di una convenzione in cui lavori, usa l’impostazione predefinita, a meno che non pensi che cambierai costantemente la struttura delle classi serializzate per qualche motivo.