Po co generować długi serialVersionUID zamiast zwykłego 1L?

210

Kiedy klasa implementuje Serializable w Eclipse, mam dwie opcje: dodaj domyślną serialVersionUID(1L)lub wygenerowaną serialVersionUID(3567653491060394677L). Myślę, że ten pierwszy jest fajniejszy, ale wiele razy widziałem ludzi używających drugiej opcji. Czy istnieje jakiś powód do wygenerowania long serialVersionUID?

IAdapter
źródło
49
Jak to dokładnie jest duplikat? Nie pytam, dlaczego w ogóle to generuje, ale dlaczego generuje długi serialVersionUID.
IAdapter
13
Kiedy Jon Skeet używa serialVersionUID, używa 0L: stackoverflow.com/questions/605828/… ;)
Hanno Fietz
8
@ HannoFietz: Dokładne zdanie brzmi: „Dla uproszczenia sugerowałbym zacząć od zera i zwiększać go o 1 za każdym razem, gdy trzeba”. Wygląda na to, że używa go 0Ltylko na początku.
LUB Mapper
7
@ORMapper: Czy sugerujesz, że Jon Skeet kiedykolwiek musi wrócić i zaktualizować napisany przez siebie kod? Nawet do niezgodności strukturalnej. Łapanie tchu! Herezja!
Thilo,

Odpowiedzi:

90

O ile mi wiadomo, byłoby to zgodne tylko z poprzednimi wersjami. Przydałoby się to tylko wtedy, gdy wcześniej zaniedbałeś użycie serialVersionUID, a następnie dokonałeś zmiany, o której wiesz, że powinna być kompatybilna, ale powoduje zerwanie serializacji.

Więcej informacji można znaleźć w specyfikacji Java Serialization Spec .

Michael Myers
źródło
69

Identyfikator UID wersji serializacji służy do śledzenia różnych wersji klasy w celu przeprowadzenia prawidłowej serializacji obiektów.

Chodzi o to, aby wygenerować identyfikator, który jest unikalny dla określonej wersji klasy, która jest następnie zmieniana, gdy do klasy dodawane są nowe szczegóły, takie jak nowe pole, które wpłynęłoby na strukturę szeregowanego obiektu.

Zawsze przy użyciu tego samego identyfikatora, na przykład 1Loznacza, że ​​w przyszłości, jeśli zmieni się definicja klasy, co spowoduje zmiany w strukturze serializowanego obiektu, istnieje duża szansa, że ​​pojawią się problemy podczas próby deserializacji obiektu.

Jeśli identyfikator zostanie pominięty, Java faktycznie obliczy dla Ciebie identyfikator na podstawie pól obiektu, ale uważam, że jest to kosztowny proces, więc podanie go ręcznie poprawi wydajność.

Oto kilka linków do artykułów omawiających serializację i wersjonowanie klas:

coobird
źródło
50
Ideą użycia 1L jest zwiększanie go za każdym razem, gdy zmieniasz właściwości lub metody klasy.
Władca
39
Umożliwianie automatycznego generowania serialversionUID nie ma wpływu na wydajność środowiska wykonawczego - jest generowane w czasie kompilacji przez javac ... jeśli zdekompilujesz kod bajtowy klasy, faktycznie zobaczysz zmienną statycznie w kodzie bajtowym.
Jared
11
Jeszcze jedna uwaga - zarządzając liczbą wyraźnie decydujesz, kiedy uważasz wersje danej wersji za „kompatybilne”, zamiast wymagać, aby definicja klasy była dokładnie taka sama.
Scott Stanchfield
23
@ Jared Zgodnie z pozycją 75 w „Skutecznej Javie Josh Blocha: wydanie drugie”: „zadeklaruj jawny identyfikator UID wersji seryjnej w każdej pisanej klasie z możliwością serializacji. Jeśli nie podano UID wersji szeregowej, wygenerowanie go w czasie wykonywania wymaga kosztownego obliczenia . ”
Colin K,
12
@coobird To wydaje się być głównym powodem, dla którego domyślny serialVersionUID nie jest zalecany 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. . Powyższy komentarz pochodzi z specyfikacji Java Object Serialization Specification 6.0
user624558 24.09.14
20

Głównym powodem wygenerowania jednego byłoby dostosowanie go do istniejącej wersji klasy, która już zachowała kopie.

Rudzik
źródło
1
OK, ale to samo będzie, jeśli zawsze mam 1L. Wszystko będzie kompatybilne, nawet jeśli dokonam jakiejkolwiek zmiany.
grep
@grep spróbuj zmienić nazwę pola, a następnie sprawdź, co się stanie.
Trejkaz
1
@Grep chodzi o to, że jeśli masz klasę, która wcześniej pomijała serialVersionUID, automatycznie wygenerowałaby ją. Więc teraz chcesz zacząć ustawiać go jawnie, ustawienie go na 1L spowoduje, że będzie on niezgodny z istniejącą klasą, a użycie wygenerowanej wartości zapewni zgodność.
marc82ch
15

„Długa” wartość domyślna serialVersionUIDjest wartością domyślną zdefiniowaną w specyfikacji Java Serialization Specification , obliczoną na podstawie domyślnego zachowania serializacji.

Więc jeśli dodasz domyślny numer wersji, twoja klasa będzie (od-) serializować szybciej, dopóki nic się nie zmieni strukturalnie, ale musisz uważać, że jeśli zmienisz klasę (dodaj / usuń pola), zaktualizujesz również numer seryjny.

Jeśli nie musisz być zgodny z istniejącymi strumieniami bitów, możesz po prostu 1Ltam umieścić i zwiększyć wersję w razie potrzeby, gdy coś się zmieni. Oznacza to, że gdy domyślna wersja serializacji zmienionej klasy będzie inna niż domyślna wersja starej klasy.

David Schmitt
źródło
11

Absolutnie powinieneś utworzyć serialVersionUID za każdym razem, gdy definiujesz klasę, która implementuje java.io.Serializable. Jeśli tego nie zrobisz, jeden zostanie dla Ciebie automatycznie utworzony, ale to źle. Automatycznie generowany serialVersionUID opiera się na sygnaturach metod twojej klasy, więc jeśli zmienisz swoją klasę w przyszłości, aby dodać metodę (na przykład), deserializacja „starych” wersji klasy zakończy się niepowodzeniem. Oto, co może się zdarzyć:

  1. Utwórz pierwszą wersję swojej klasy bez definiowania serialVersionUID.
  2. Serializuj wystąpienie swojej klasy do trwałego sklepu; serialVersionUID jest generowany automatycznie.
  3. Zmodyfikuj klasę, aby dodać nową metodę, i ponownie wdróż aplikację.
  4. Próba deserializacji instancji, która została zserializowana w kroku 2, ale teraz kończy się niepowodzeniem (kiedy powinna się powieść), ponieważ ma ona inny automatycznie wygenerowany serialVersionUID.
Pankaj Kumar
źródło
1
W rzeczywistości deserializacja starych wersji klasy powinna rzeczywiście zawieść, ponieważ nie są już takie same. Sugeruje się samodzielne wygenerowanie serialVersionUID, aby zapobiec awariom (de) serializacji przy zmianie sygnatury klasy. Chociaż twoja sugestia jest właściwa, wyjaśnienie jej celu jest po prostu błędne i mylące. Mądrze byłoby zmodyfikować swoją odpowiedź.
mostruash
6

Jeśli nie określisz identyfikatora serialVersionUID, wówczas Java tworzy go w locie. Wygenerowany serialVersionUID to ten numer. Jeśli zmienisz coś w swojej klasie, co tak naprawdę nie sprawia, że ​​twoja klasa jest niezgodna z poprzednimi serializowanymi wersjami, ale zmienia skrót, musisz użyć wygenerowanego bardzo dużej liczby serialVersionUID (lub „oczekiwanej” liczby z komunikatu o błędzie) . W przeciwnym razie, jeśli sam wszystko śledzisz, 0, 1, 2 ... jest lepszy.

współpracownik
źródło
Miałeś na myśli ==> 1. Jeśli chcesz, aby różne zmiany klas były kompatybilne, skorzystaj z wygenerowanej. 2. Jeśli chcesz, aby różne wersje klas były niezgodne, użyj domyślnej i zachowaj ostrożność przy zwiększaniu. Czy zrozumiałem to poprawnie?
JavaDeveloper
4

Kiedy używasz serialVersionUID (1L) zamiast generowania serialVersionUID (3567653491060394677L), coś mówisz.

Mówisz, że masz 100% pewności, że żaden system, który nigdy nie dotknie tej klasy, miałby niezgodną wersję tej klasy z serializacją o numerze wersji 1.

Jeśli wymyślisz jakieś usprawiedliwienie dla nieznanej historii wersji serializowanej, może to być trudne do powiedzenia z pewnością. Za jego życia wiele osób będzie utrzymywać udaną klasę, żyć w wielu projektach i przebywać w wielu systemach.

Możesz nad tym męczyć. Lub możesz zagrać na loterii, mając nadzieję na przegraną. Jeśli wygenerujesz wersję, masz małe szanse, że coś pójdzie nie tak. Jeśli założysz „Hej, założę się, że nikt jeszcze nie użył 1”, Twoje szanse są większe niż małe. Właśnie dlatego, że wszyscy uważamy, że 0 i 1 są fajne, masz większe szanse na ich trafienie.

-

Kiedy generujesz serialVersionUID (3567653491060394677L) zamiast używać serialVersionUID (1L), coś mówisz.

Mówisz, że ludzie mogli ręcznie tworzyć lub generować inne numery wersji w historii tej klasy i nie obchodzi cię to, ponieważ Longs cholernie duże liczby.

Tak czy inaczej, chyba że doskonale znasz historię numerów wersji używanych do serializacji klasy w całym wszechświecie, w którym istnieje lub będzie istnieć, ryzykujesz. Jeśli masz czas, aby upewnić się w 100%, że 1 to AOK, idź. Jeśli to za dużo pracy, śmiało wygeneruj liczbę. Bardziej prawdopodobne jest, że wygrasz na loterii niż popełnisz błąd. Jeśli tak, daj mi znać, a kupię ci piwo.

Po tych wszystkich rozmowach na temat gry w loterię mogłem sprawiać wrażenie, że serialVersionUID jest generowany losowo. W rzeczywistości, o ile zakres liczb jest równomiernie rozłożony na każdą możliwą wartość Długości, co byłoby w porządku. Jednak tak naprawdę dzieje się tak:

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

Jedyną różnicą, jaką możesz z tym zrobić, jest to, że nie potrzebujesz źródła losowego. Używasz zmian w samej klasie, aby zmienić wynik. Ale zgodnie z zasadą szufladki nadal istnieje szansa, że ​​może się nie udać i dojść do kolizji. To jest po prostu niezwykle mało prawdopodobne. Życzę powodzenia w wydostaniu ze mnie piwa.

Jednak nawet jeśli klasa będzie żyła tylko w jednym systemie i jednej bazie kodu, myślenie, że ręczne zwiększenie liczby daje zerową szansę na kolizję, oznacza po prostu, że nie rozumiesz ludzi. :)

Jakiś facet
źródło
Jeśli „system” dotknie klasy, tj. Zmieni klasę w taki sposób, że serializacja stanie się niezgodna, wówczas pojawia się pytanie, czy ten system zmieni również serialVersionUID. Nie sądzę, że szanse są mniejsze, że pamięta się je zmieniać, gdy będzie długi. Myślę, że wręcz przeciwnie, prawda jest taka, że ​​jeśli liczby łatwiej zapamiętać, zmiany są wyższe, to zauważam, że przypadkowo ich nie zmieniłem.
Reto Gmür,
2
To nieprawda! Kiedy generujesz serialVersionUID i deklarujesz tę wartość w kodzie źródłowym, zamiast 1L lub nic, co tak naprawdę mówisz: chcę potencjalnie niewykrytej kolizji w przyszłości z nieokreślonymi efektami i nie chcę, aby java lub inni ludzie temu zapobiegali . Java jest paranoiczna, ale posłuszna. Ludzie zwykle nie bawią się dużymi liczbami. W ten sposób, gdy zmienia się klasa, java może nadal dokonać serializacji starych niekompatybilnych wersji. MwoaHaHa ...;)
Superole,
1

Cóż, serialVersionUID jest wyjątkiem od reguły, że „pola statyczne nie są serializowane”. ObjectOutputStream zapisuje za każdym razem wartość serialVersionUID do strumienia wyjściowego. ObjectInputStream odczytuje go z powrotem i jeśli wartość odczytana ze strumienia nie zgadza się z wartością serialVersionUID w bieżącej wersji klasy, wówczas zgłasza wyjątek InvalidClassException. Ponadto, jeśli nie ma oficjalnie zadeklarowanego serialVersionUID w klasie do serializacji, kompilator automatycznie dodaje go z wartością wygenerowaną na podstawie pól zadeklarowanych w klasie.

Szczęściarz
źródło
0

Ponieważ w wielu przypadkach domyślny identyfikator nie jest unikalny. więc tworzymy id do stworzenia unikalnej koncepcji.

Pushpendra Kuntal
źródło
Czy możesz zredagować swoją odpowiedź, aby bardziej ją uwydatnić? To wydaje się tutaj komentarzem. Dzięki.
Gray
0

Aby dodać do @David Schmitts odpowiedź, jako ogólną zasadę zawsze używałbym domyślnej 1L poza konwencją. Musiałem tylko cofnąć się i zmienić niektóre z nich kilka razy, ale wiedziałem o tym, kiedy dokonałem zmiany i aktualizowałem domyślny numer o jeden za każdym razem.

W mojej obecnej firmie wymagają one automatycznie wygenerowanego numeru, więc używam go do konwencji, ale wolę domyślny. Uważam, że jeśli nie jest to konwencja, w której pracujesz, użyj domyślnej, chyba że uważasz, że z jakiegoś powodu będziesz ciągle zmieniać strukturę swoich serializowanych klas.

James Drinkard
źródło
0

Identyfikator UID wersji serializacji służy do śledzenia różnych wersji klasy w celu przeprowadzenia prawidłowej serializacji obiektów.

Chodzi o to, aby wygenerować identyfikator, który jest unikalny dla określonej wersji klasy, która jest następnie zmieniana, gdy do klasy dodawane są nowe szczegóły, takie jak nowe pole, które wpłynęłoby na strukturę szeregowanego obiektu.

Proste wyjaśnienie:

Czy serializujesz dane?

Serializacja polega zasadniczo na zapisywaniu danych klasy do pliku / strumienia / etc. Dezserializacja polega na odczytywaniu tych danych z powrotem do klasy.

Czy zamierzasz wejść do produkcji?

Jeśli tylko testujesz coś z nieistotnymi / fałszywymi danymi, nie przejmuj się tym (chyba że bezpośrednio testujesz serializację).

Czy to jest pierwsza wersja?

Jeśli tak, ustaw serialVersionUID = 1L.

Czy to druga, trzecia itd. Wersja produktu?

Teraz musisz się martwić o serialVersionUID i powinieneś zajrzeć do niego dogłębnie.

Zasadniczo, jeśli nie zaktualizujesz wersji poprawnie podczas aktualizacji klasy, musisz zapisać / odczytać, podczas próby odczytania starych danych pojawi się błąd.

Saurabh Verma
źródło