Kas yra serialVersionUID ir kodėl turėčiau jį naudoti?

„Eclipse“ įspėja, kai trūksta serialVersionUID .

„Foo Serializable Class“ neatpažįsta „Static Final Field“ ilgojo tipo serialVersionUID

Kas yra serialVersionUID ir kodėl tai svarbu? Parodykite pavyzdį, kuriame trūksta serialVersionUID .

2641
13 нояб. nustatyti ashokgelal lapkričio 13 2008-11-13 02:24 '08 at 2:24 am 2008-11-13 02:24
@ 24 atsakymai

Dokumentai java.io.Serializable yra tikriausiai maždaug taip pat gerai, kaip paaiškinimas:

Serializacijos vykdymo trukmė susieja su kiekviena serializuojama klase versijos numeriu, vadinamu serialVersionUID , kuris naudojamas deserializacijos metu, siekiant patikrinti, ar serializuoto objekto siuntėjas ir gavėjas yra šio objekto, suderinamo su serializavimu, klasės. Jei imtuvas įkelia klasės objektą, turintį serialVersionUID skirtingą nuo atitinkamos siuntėjo klasės klasės, deserializacija sukels InvalidClassException . serialVersionUID klasė gali deklaruoti savo serialVersionUID aiškiai deklaruodama lauką pavadinimu serialVersionUID kuris turi būti statinis, baigtinis ir tipo long :

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

Jei serializuota klasė aiškiai serialVersionUID , tuomet serializavimo trukmė apskaičiuoja šios klasės numatytąjį serialVersionUID vertę, remdamasi įvairiais klasės aspektais, kaip aprašyta „Java“ objektų serijavimo specifikacijoje (TM). Vis dėlto primygtinai rekomenduojama, kad visos serializuotos klasės aiškiai deklaruotų serialVersionUID , nes serialVersionUID skaičiavimas pagal nutylėjimą yra labai jautrus klasės duomenims, kurie gali skirtis priklausomai nuo kompiliatoriaus įgyvendinimo, todėl gali būti netikėta InvalidClassExceptions deserializacijos. Todėl, siekiant užtikrinti nuoseklų serialVersionUID reikšmę įvairiuose „Java“ kompiliatoriaus serialVersionUID , klasifikuojama klasė turi deklaruoti aiškią serialVersionUID reikšmę. Taip pat primygtinai rekomenduojama, kad aiškios serialVersionUID deklaracijose būtų naudojamas privatus modifikatorius, jei tai įmanoma, nes tokios deklaracijos taikomos tik iš karto deklaruotiems klasės laukams. serialVersionUID nėra naudojami kaip paveldėti nariai.

2021 m
13 нояб. Atsakymą pateikė Jon Skeet lapkričio 13 d 2008-11-13 02:30 '08, 2:30, 2008-11-13 02:30

Jei serializuojate tik todėl, kad jums reikia serializuoti įgyvendinimui (kas rūpinasi, jei serializuojate HTTPSession, pavyzdžiui ... jei jis yra saugomas, ar ne, jums tikriausiai nereikia deformuoti formos objekto), tada galite jį ignoruoti .

Jei naudojate serializaciją, tai svarbu tik tuo atveju, jei planuojate saugoti ir gauti objektus, naudodami serializaciją. SerialVersionUID atstovauja jūsų klasės versiją, ir turėtumėte ją padidinti, jei dabartinė jūsų klasės versija neatitinka ankstesnės versijos.

Daugeliu atvejų jūs tikriausiai nenaudosite serializacijos tiesiogiai. Jei taip, sukurkite numatytąjį uid serializuotiną spustelėdami sparčiosios taisymo parinktį ir nesijaudinkite.

429
13 нояб. atsakymą pateikė MetroidFan2002 lapkritis 13. 2008-11-13 07:24 '08, 7:24 am 2008-11-13 07:24

Aš negaliu praleisti šios galimybės prijungti knygą „Josh Bloch Effective Java“ (2-asis leidimas). 11 skyrius yra nepakeičiamas „Java“ serializavimo šaltinis.

Per Josh automatiškai sukurtas UID sukuriamas pagal klasės pavadinimą, įdiegtas sąsajas ir visus viešuosius bei saugomus narius. Bet kokio jų keitimas pakeis serialVersionUID . Todėl jums nereikia jaustis su jais, nebent esate tikri, kad daugiau nei viena klasės versija bus serializuota (arba per procesus, arba vėliau).

Jei jūs juos ignoruosite ir vėliau pamatysite, kad reikia kažkaip keisti klasę, bet išlaikyti suderinamumą su senosios klasės versija, galite naudoti JDK serijos redaktoriaus įrankį serialVersionUID senojoje klasėje ir aiškiai įdiegti ją naujoje klasėje. (Priklausomai nuo jūsų pakeitimų, jums gali tekti įdiegti pasirinktinį serializavimą pridedant „ writeObject ir „ readObject - žr. „ Serializable readObject arba minėtą 11 skyrių.

275
13 нояб. Atsakymą pateikė Scott Bale lapkričio 13 d. 2008-11-13 02:37 '08 at 2:37 am 2008-11-13 02:37

„Eclipse“ galite pasakyti, kad ignoruos šiuos serijosVersionUID įspėjimus:

>

Jei nežinote, yra daug kitų įspėjimų, kuriuos galite įtraukti į šį skyrių (arba net kai kurie iš jų yra pranešami kaip klaidos), kurių daugelis yra labai naudingi:

  • Galimos programavimo problemos: galimas atsitiktinis loginis priskyrimas
  • Galimos programavimo problemos: prieiga prie nulinio žymeklio.
  • Nereikalingas kodas: Vietinis kintamasis niekada neskaito.
  • Nereikalingas kodas: nereikalingas patikrinimas
  • Nereikalingas kodas: nereikalingas sąrašas arba „pavyzdys“

ir daugelis kitų.

125
13 нояб. Atsakymą pateikia matt b . 2008-11-13 04:17 '08 4:17 am 2008-11-13 04:17

serialVersionUID leidžia lengvai valdyti serializuotų duomenų versijas. Jo vertė yra saugoma su duomenimis serializavimo metu. Išjungus seriją, patikrinama ta pati versija, nes serijiniai duomenys atitinka dabartinį kodą.

Jei norite keisti savo duomenis, paprastai pradedate nuo serialVersionUID nuo 0 ir paspauskite jį su kiekvienu savo klasės struktūriniu pakeitimu, kuris keičia serializuotus duomenis (pridedant arba pašalinant ne pereinamuosius laukus).

Įmontuotas de-serializacijos mechanizmas ( in.defaultReadObject() ) atsisakys atlikti serializavimą iš senesnių duomenų versijų. Bet jei norite, galite nustatyti savo readObject () funkciją, kuri gali skaityti senus duomenis. Tuomet šis vartotojo kodas gali patikrinti serialVersionUID kad sužinotumėte, kokia versija yra duomenų, ir nuspręskite, kaip ją išjungti. Šis versijos valdymo metodas yra naudingas, jei saugote serializuotus duomenis, saugomus keliose kodo versijose.

Tačiau serializuotų duomenų saugojimas tokiam ilgam laikotarpiui nėra labai dažnas. Daug dažniau serializavimo mechanizmas naudojamas laikinai rašyti duomenis, pavyzdžiui, talpyklą arba siųsti ją per tinklą į kitą programą, turinčią tą pačią kodo bazės dalių versiją.

Tokiu atveju nesate suinteresuotas išlaikyti atgalinį suderinamumą. Jums reikia tik įsitikinti, kad kodai, kuriais faktiškai keičiasi pranešimai, turi tas pačias atitinkamų klasių versijas. Siekiant palengvinti tokį patikrinimą, turėtumėte palaikyti serialVersionUID taip pat, kaip ir anksčiau, ir nepamirškite ją atnaujinti, kai darote pakeitimus savo klasėse.

Jei pamiršote atnaujinti lauką, galite gauti dvi skirtingų klasių versijas su kita struktūra, tačiau su tuo pačiu serialVersionUID . Jei taip atsitiks, numatytasis mechanizmas ( in.defaultReadObject() ) neaptiks skirtumo ir bandys nenuosekliuoti duomenų. Dabar galite susidurti su kritine vykdymo klaida arba tyliu gedimu (tuščiais laukais). Tokias klaidų rūšis gali būti sunku rasti.

Taigi, norint padėti naudoti šį įrenginį, „Java“ platforma siūlo jums galimybę pasirinkti rankiniu būdu neinstaliuoti serialVersionUID . Vietoj to, kompiliavimo metu bus sukurtas klasės struktūros maišas ir naudojamas kaip ID. Šis mechanizmas užtikrina, kad niekada neturėsite skirtingų klasių struktūrų su tuo pačiu identifikatoriumi, todėl jūs negausite pirmiau minėtų nesėkmingų bandomųjų laiko serializavimo bandymų.

Bet yra automatiškai sukurta id strategija. Būtent, kad tos pačios klasės generuojami identifikatoriai kompiliatoriuose gali skirtis (kaip minėta pirmiau, John Skit). Todėl, jei perduodate serijinius duomenis tarp kodų, sukompiliuotų su skirtingais kompiliatoriais, rekomenduojama bet kuriuo atveju rankiniu būdu išlaikyti identifikatorius.

Ir jei esate atgalinis suderinamumas su jūsų duomenimis, kaip ir pirmiau minėtu atveju, tikriausiai norite išsaugoti identifikatorių. Tai yra, kad būtų galima nuskaityti identifikatorius ir kontroliuoti, kada ir kaip jie keičiasi.

100
03 окт. Atsakyti Alexander Torstling 03 spalis. 2012-10-03 09:05 '12, 09:05 am 2012-10-03 09:05

Kas yra serialVersionUID ir kodėl turėčiau jį naudoti?

SerialVersionUID yra unikalus identifikatorius kiekvienai klasei, JVM jį naudoja, kad palygintų tos klasės klases, kurios serijinės versijos metu įkelia tą pačią klasę deserializacijos metu.

Nurodant vieną suteikia daugiau kontrolės, nors JVM ją sukuria, jei nenurodote. Gauta vertė gali skirtis tarp skirtingų kompiliatorių. Be to, kartais jūs tiesiog norėsite dėl tam tikrų priežasčių uždrausti senų serializuotų objektų deserializavimą [ backward incompatibility ], tokiu atveju jums reikia tiesiog pakeisti serijosVersionUID.

Javadocs for Serializable sako :

Numatytasis serialVersionUID skaičiavimas yra labai jautrus klasės detalėms, kurios gali skirtis priklausomai nuo kompiliatoriaus įgyvendinimo ir todėl gali sukelti netikėtą InvalidClassException per deserializaciją.

Todėl turėtumėte paskelbti serijosVersionUID, nes jis suteikia mums daugiau kontrolės .

Šiame straipsnyje yra keletas gerų dalykų šia tema.

64
17 окт. Atsakyti Thalaivar spalio 17 d 2013-10-17 07:28 '13, 7:28, 2013-10-17 07:28

Pradiniame klausime buvo užduotas klausimas, kodėl tai svarbu ir pavyzdys, kai šis Serial Version ID bus naudingas. Na, aš rasiu vieną.

Tarkime, jūs sukuriate Car klasę, sukuriate jos egzempliorių ir parašėte jį į objektų srautą. Lygintas automobilio objektas tam tikrą laiką yra failų sistemoje. Tuo tarpu, jei keičiasi Car klasė, pridėkite naują lauką. Vėliau, kai bandysite skaityti (pvz., Deserialize) išlygintą Car objektą, gausite java.io.InvalidClassException nes visos serializuojamos klasės automatiškai priskiriamos unikaliam identifikatoriui. Ši išimtis išmesta, kai klasės identifikatorius nėra lygus išlyginto objekto identifikatoriui. Jei iš tiesų apie tai galvojate, išimtis iškeliama dėl to, kad įterptas naujas laukas. Galite išvengti šios išimties valdydami patys versijas, deklaruodami aiškų serijosVersionUID. Taip pat yra nedidelis našumo pranašumas, jei aiškiai serialVersionUID savo serialVersionUID (nes nereikia apskaičiuoti). Todėl rekomenduojame pridėti savo serijinės versijos ID savo serijinėms klasėms, kai tik jas sukuriate, kaip parodyta žemiau:

 public class Car { static final long serialVersionUID = 1L; //assign a long value } 
45
07 апр. atsakymas, kurį pateikė Rupesh 07 Bal 2013-04-07 13:43 '13, 13:43, 2013-04-07 13:43

Jei gausite šį įspėjimą klasėje, kurią niekada negalvojote apie serializaciją, ir kad jūs nepranešėte apie save implements Serializable , tai dažnai yra todėl, kad paveldėjote iš superklasės, įgyvendinančios Serializable. Dažnai tai būtų geriau perduoti tokį objektą, o ne naudoti paveldėjimą.

Taigi

 public class MyExample extends ArrayList<String> { public MyExample() { super(); } ... } 

daryti

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

ir skambinkite myList.foo() vietoj this.foo() (arba super.foo() ) atitinkamuose metoduose. (Tai nėra tinkama visais atvejais, bet vis dar gana dažnai.)

Dažnai matau, kad žmonės plečiasi JFrame, arba kai jiems tai tik reikia perduoti. (Jis taip pat padeda automatiškai užpildyti IDE, nes JFrame yra šimtai būdų, kurių jums nereikia, kai norite savo skambinti savo klasėje.)

Vienas atvejis, kai neišvengiamas įspėjimas (arba serijosVersionUID), yra tada, kai išplečiate nuo AbstractAction, paprastai anoniminėje klasėje, pridedant tik actionPerformed metodą. Manau, kad šiuo atveju neturėtų būti jokių įspėjimų (nes jūs paprastai negalite patikimai serializuoti ir deserialiuoti tokių anoniminių klasių bet kuriuo atveju skirtingose ​​klasių versijose), bet nesu įsitikinęs, kaip kompiliatorius jį atpažino.

33
03 февр. Atsakymą pateikė Paŭlo Ebermann, vasario 3 d. 2011-02-03 03:32 '11 at 3:32 2011-02-03 03:32

Jei niekada nereikės serializuoti savo objektų į baitų masyvą ir juos siųsti / išsaugoti, jums nereikia jaudintis. Jei taip, turite atsižvelgti į savo serialVersionUID, nes objekto deserializatorius atitiks jo klasės krovinio turinio objektą. Daugiau apie tai skaitykite „Java“ kalbos specifikacijoje.

32
13 нояб. Atsakymą pateikė eishay lapkričio 13 d. 2008-11-13 02:30 '08, 2:30, 2008-11-13 02:30

Norėdami suprasti serijosVersionUID lauko vertę, turite suprasti, kaip veikia serializacija / deserializacija.

Kai serijinis klasės serijos objektas yra serializuotas, „Java Runtime“ susieja versijos serijos numerį (pavadintą serialVersionUID) su šiuo serijiniu objektu. Tuo metu, kai deserialijuojate šį serializuotą objektą, „Java Runtime“ atitinka serializuoto objekto serijosVersionUID su šios klasės serijosVersionUID. Jei abu yra lygūs, tada tik jis pereina į tolesnį deserizacijos procesą, kitaip mesti InvalidClassException.

Taigi, darome išvadą, kad sėkmingai užbaigiant serializacijos / deserizacijos procesą serijinis objektas serialVersionUID turi būti lygiavertis šios klasės serijosVersionUID. Jei programuotojas aiškiai nurodo serialVersionUID reikšmę programoje, tada ta pati vertė bus susieta su serijiniu objektu ir klase, nepriklausomai nuo serializavimo ir deserializacijos platformos (pvz., Serializavimą galima atlikti platformoje, pvz., Naudojant >

Tačiau jei programuotojas nenurodo serijosVersionUID, tada atliekant serializaciją Bet kurio objekto serializavimas, „Java“ vykdymo trukmė apskaičiuoja savo algoritmą. Šis algoritmas, skirtas skaičiuoti serijinįVersionUID, skiriasi nuo vieno JRE. Taip pat įmanoma, kad aplinka, kurioje objektas yra serializuotas, naudoja vieną JRE (pavyzdžiui, SUN JVM) ir aplinką, kurioje vyksta deserializacija, naudojant Linux Jvm (zing). Tokiais atvejais serijinis objektas, susietas su serijiniu objektu, skirsis nuo serijosVersionUID klasės, apskaičiuotos deserializacijos aplinkoje. Savo ruožtu deserializacija nebus sėkminga. Todėl, norint išvengti tokių situacijų / problemų, programuotojas visada turėtų nurodyti serijinės klasės serijosVersionUID.

21
02 июня '13 в 9:20 2013-06-02 09:20 atsakymas pateikiamas Nitesh Soni 02 birželio 13 d. 9:20 2013-06-02 09:20

Pavyzdžiui, kai trūksta serijosVersionUID gali sukelti problemą:

Dirbu su šia „Java EE“ programa, kurią sudaro žiniatinklio modulis, kuriame naudojamas EJB modulis. Žiniatinklio modulis EJB modulį POJO nuotoliniu būdu ir perduoda POJO , kuris įgyvendina Serializable kaip argumentą.

Ši POJO's klasė buvo supakuota EJB banko viduje ir savo banke WEB-INF / lib žiniatinklio modulyje. Jie iš tikrųjų yra tokie patys, bet kai pakuočiu EJB modulį, aš išpakuosu šį POJO banerį, kad pakuotė būtų su EJB moduliu.

Skambinimas į EJB nepavyko toliau pateiktos išimties, nes nepranešiau apie savo 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 
18
20 мая '13 в 22:32 2013-05-20 22:32 atsakymą pateikė Henrique Ordine gegužės 20 d., 13 val. 10:32 2013-05-20 22:32

Nesijaudinkite, numatytasis skaičiavimas yra tikrai geras ir pakankamas 99,9999% atvejų. Ir jei turite problemų, galite - kaip jau minėjote - įvesti UID kaip reikia (tai yra mažai tikėtina)

18
29 апр. Atsakymas, kurį pateikė grand johnson Apr 29 2012-04-29 18:59 '12, 18:59, 2012-04-29 18:59

Serializavimo aplinka susieja kiekvieną serializuojamą klasę su versijos numeriu, vadinamu serialVersionUID, kuris naudojamas deserializacijos metu, siekiant patikrinti, ar serializuoto objekto siuntėjas ir gavėjas yra šio objekto, kuris yra suderinamas su serializavimu, pakrovimo klasės.

Jei imtuvas įkelia objekto klasę, turinčią skirtingą serijinę versijąUID nei atitinkama siuntėjo klasė, tada deserializacija sukels InvalidClassException.

Serialable klasė gali deklaruoti savo serijosVersionUID aiškiai deklaruodama lauką, pavadintą serialVersionUID , kuris turi būti statinis, galutinis ir ilgas:

 ANY-ACCESS-MODIFIER static final long serialVersionUID = 42L; 
16
28 февр. atsakymas suteikiamas auklėtojui 28 vasaris. 2014-02-28 11:35 '14 at 11:35 2014-02-28 11:35

Paprastai aš naudoju serialVersionUID viename kontekste: kai žinau, kad jis palieka „Java VM“ kontekstą.

Norėčiau tai žinoti, kai naudoju „ ObjectInputStream ir „ ObjectOutputStream “ mano paraiškai, arba jei žinau, kad naudojama biblioteka / rėmas. SerialVersionID užtikrina, kad įvairios skirtingų versijų ar tiekėjų „Java“ virtualiosios mašinos sąveikauja teisingai, arba jei jos yra saugomos ir atsiųstos už VM, pvz., „ HttpSession , sesijos duomenys gali išlikti net iš naujo paleidžiant ir atnaujinant taikomąją programą.

Visais kitais atvejais

 @SuppressWarnings("serial") 

nes didžiąją laiko dalį numatytasis serialVersionUID pakanka. Tai apima Exception , „ HttpServlet .

15
04 марта '14 в 19:17 2014-03-04 19:17 atsakymą pateikė Archimedas Trajano kovo 14 d. 14:17 2014-03-04 19:17

Pirmiausia reikia paaiškinti serializaciją.
Serializacija leidžia konvertuoti objektą į srautą, siųsti šį objektą per tinklą ARBA Išsaugoti į failą ARBA įrašyti į DB naudojimui raidėmis.

Yra serializavimo taisyklės .

  • Objektas serializuojamas tik tuo atveju, jei jo klasė ar superklasė įgyvendina Serializable sąsają.

  • Objektas yra serializuojamas (jis pats įgyvendina Serializable sąsają), net jei jo klasė nėra. Tačiau pirmoji klasė serijinės klasės, kuri neįgyvendina Serializable sąsajos, hierarchijoje, turi turėti ne-arg konstruktorių. Jei tai pažeidžiama, readObject () bus paleista java.io.InvalidClassException išimtis vykdymo metu.

  • Visi primityvūs tipai yra serializuojami.

  • Laikinieji laukai (su pereinamuoju modifikatoriumi) NĖRA serializuoti (t. Y. Nėra išsaugoti ar atstatyti). Klasė, įgyvendinanti Serializable, turi pažymėti pereinamuosius laukus klasėms, kurios nepalaiko serializacijos (pvz., Failų srautas).

  • Statiniai laukai (su statiniu modifikatoriumi) nėra serializuoti.