Навіщо генерувати довгий serialVersionUID замість простого 1L?


210

Коли клас реалізує Serializable в Eclipse, у мене є два варіанти: додати за замовчуванням serialVersionUID(1L)або згенерувати serialVersionUID(3567653491060394677L). Я думаю, що перший крутіший, але я багато разів бачив людей, які використовують другий варіант. Чи є причина для генерації long serialVersionUID?


49
Як це точний дублікат? Я не запитую, навіщо генерувати його взагалі, а чому генерувати довгий serialVersionUID.
IAdapter

13
Коли Джон Скіт використовує serialVersionUID, він використовує 0L: stackoverflow.com/questions/605828/… ;)
Hanno Fietz

8
@HannoFietz: Точне речення: "Для простоти я б запропонував починати з 0 і збільшувати його на 1 кожен раз, коли потрібно". Отже, здається, що він використовує 0Lлише спочатку.
АБО Mapper

7
@ORMapper: Ви маєте на увазі, що Джон Скіт коли-небудь потребує повернення та оновлення написаного ним коду? Навіть до структури структурної несумісності. Гасп! Єресь!
Тіло

Відповіді:


90

Наскільки я можу сказати, це було б лише для сумісності з попередніми випусками. Це буде корисно лише у тому випадку, якщо ви знехтували раніше використовувати serialVersionUID, а потім внесли зміни, які, на вашу думку, повинні бути сумісними але які призводять до порушення серіалізації.

Докладнішу інформацію див. У розділі Спеціалізація Java .


69

Мета UID версії серіалізації - відстежувати різні версії класу з метою виконання дійсної серіалізації об'єктів.

Ідея полягає у створенні ідентифікатора, унікального для певної версії класу, який потім змінюється, коли до класу додаються нові деталі, наприклад, нове поле, яке вплине на структуру серіалізованого об'єкта.

Завжди використовуючи той самий ідентифікатор, як, наприклад, 1Lозначає, що в майбутньому, якщо визначення визначення класу буде змінено, що спричинить зміни в структурі серіалізованого об'єкта, буде хороший шанс виникнути проблеми при спробі десеріалізації об'єкта.

Якщо ідентифікатор опущений, Java фактично обчислить ідентифікатор для вас на основі полів об’єкта, але я вважаю, що це дорогий процес, тому надання одного вручну підвищить продуктивність.

Ось декілька посилань на статті, які обговорюють серіалізацію та версію класів:


50
Ідея використання 1L полягає в тому, що ви збільшуєте його щоразу, коли змінюєте властивості або методи класу.
Powerlord

39
Немає впливу на продуктивність виконання, щоб дозволити автоматичну генерацію serialversionUID - він генерується під час компіляції javac ... якщо ви декомпілюєте байт-код класу, ви фактично побачите змінну статично в байт-коді.
Джаред

11
Ще одна примітка - керуючи номером явно, ви приймаєте рішення, коли вважаєте версії класу "сумісними", а не вимагати визначення класу таким самим.
Скотт Стенчфілд

23
@ Джаред Відповідно до Пункту 75 Ефективної Java Джоша Блоха: 2-е видання: "оголосити явну послідовну версію UID у кожному серіалізаційному класі, який ви пишете .... Якщо не вказано UID серійної версії, для створення цього під час виконання потрібні дорогі обчислення. . "
Колін К

12
@coobird Це, мабуть, є основною причиною того, що serialVersionUID за замовчуванням не рекомендується. 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. Зазначений вище коментар був узятий із специфікації специфікації серіалізації Java версії 6.0
user624558

20

Основною причиною для створеного буде його сумісність з існуючою версією класу, яка вже зберігає копії.


1
Гаразд, але те саме буде, якщо у мене завжди 1л. Все буде сумісно, ​​навіть якщо я внесу якісь зміни.
греп

@grep спробуйте перейменувати поле, а потім подивіться, що відбувається.
Трежказ

1
@grep справа в тому, що якщо у вас є клас, який раніше опускав serialVersionUID, він отримав би створений автоматично. Отже, тепер ви хочете почати налаштовувати його явно, встановлення його на 1L зробить його несумісним із існуючим класом, тоді як використання створеного значення зберігає його сумісним.
marc82ch

15

"Довгий" за замовчуванням serialVersionUID- це значення за замовчуванням, як визначено специфікацією Java-серіалізації , обчислене з поведінки серіалізації за замовчуванням.

Отже, якщо ви додасте номер версії за замовчуванням, ваш клас (де-) серіалізуватиметься швидше, доки нічого структурно не зміниться, але вам доведеться подбати про те, що при зміні класу (додавання / видалення полів) ви також оновите оновлення серійний номер.

Якщо вам не потрібно бути сумісним з існуючими бітовими потоками, ви можете просто помістити 1Lйого та збільшити версію за потребою, коли щось зміниться. Тобто, коли версія серіалізації за замовчуванням зміненого класу відрізнятиметься від типової версії старого класу.


11

Ви абсолютно повинні створювати serialVersionUID кожного разу, визначаючи клас, який реалізується java.io.Serializable. Якщо цього не зробити, один буде створений для вас автоматично, але це погано. Автоматично створений serialVersionUID заснований на методах підписів вашого класу, тому, якщо ви в майбутньому зміните свій клас, щоб додати метод (наприклад), десеріалізація "старих" версій класу не вдасться. Ось що може статися:

  1. Створіть першу версію свого класу, не визначаючи serialVersionUID.
  2. Серіалізувати екземпляр свого класу до постійного магазину; serialVersionUID автоматично генерується для вас.
  3. Змініть свій клас, щоб додати новий метод, і перерозподіліть свою програму.
  4. Спроба дезаріалізувати екземпляр, який був серіалізований на кроці 2, але зараз він не вдається (коли він повинен досягти успіху), оскільки він має інший автоматично створений serialVersionUID.

1
Власне кажучи, дезаріалізація старих версій класу справді повинна бути невдалою, оскільки вони вже не однакові. Ви пропонуєте генерувати serialVersionUID самостійно для запобігання (де) збоїв серіалізації при зміні підпису класу. Хоча ваша пропозиція доречна, ваше пояснення її мети просто неправильне та оманливе. Було б розумно змінити свою відповідь.
mostruash

6

Якщо ви не вказали serialVersionUID, тоді Java робить його на льоту. Створений serialVersionUID - це число. Якщо ви змінили щось у своєму класі, що насправді не робить ваш клас несумісним з попередніми серіалізованими версіями, але змінює хеш, то вам потрібно використовувати згенеровану дуже велику кількість serialVersionUID (або "очікуване" число з повідомлення про помилку) . Інакше, якщо ви самі все відстежуєте, 0, 1, 2 ... краще.


Ви мали на увазі ==> 1. Якщо ви хочете, щоб різні зміни класів були сумісні, використовуйте створений. 2. Якщо ви хочете, щоб різні версії класів були несумісними, використовуйте типовий і будьте обережні у збільшенні. Я правильно це зрозумів?
JavaDeveloper

4

Коли ви використовуєте serialVersionUID (1L), а не створюєте serialVersionUID (3567653491060394677L), ви щось говорите.

Ви говорите, що на 100% впевнені, що жодна система, яка ніколи не торкнеться цього класу, не має сумісної серіалізованої версії цього класу з номером версії 1.

Якщо ви можете придумати будь-який привід для того, щоб його серіалізована історія версій була невідомою, це може бути важко сказати з упевненістю. За все життя успішний клас підтримуватимуть багато людей, житимуть у багатьох проектах та проживатимуть у багатьох системах.

Ви можете мучитися через це. Або ви можете грати в лотерею, сподіваючись програти. Якщо ви генеруєте версію, у вас є невеликі шанси, що все піде не так. Якщо ви припускаєте, "Ей, я вважаю, ще ніхто не використовував 1", шанси вищі, ніж крихітні. Саме тому, що всі ми думаємо, що 0 і 1 круто, ви маєте більші шанси їх вразити.

-

Коли ви генеруєте serialVersionUID (3567653491060394677L), а не використовуєте serialVersionUID (1L), ви щось говорите.

Ви кажете, що люди можуть створити або вручну створити інші номери версій за всю історію цього класу, і вам не байдуже, бо Лонги вигадують великі цифри.

У будь-якому випадку, якщо ви прекрасно не знаєте історію номерів версій, що використовуються при серіалізації класу у всьому Всесвіті, де він є або коли-небудь буде існувати, ви ризикуєте. Якщо у вас є час на 100% впевнитись, що 1 АОК, продовжуйте це робити. Якщо вам доведеться багато працювати, продовжуйте та сліпо генеруйте число. Ви швидше виграєте в лотерею, ніж помилитесь. Якщо це станеться, дайте мені знати, і я куплю вам пиво.

З усією цією розмовою про гру в лотерею я, можливо, створив у вас враження, що serialVersionUID генерується випадковим чином. Насправді, поки діапазон чисел рівномірно розподілений по кожному можливому значенню Довгого, це було б добре. Однак насправді робиться так:

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

Єдина відмінність, яку ви отримуєте в цьому, - вам не потрібно джерело випадкових випадків. Ви використовуєте зміни в самому класі, щоб змінити результат. Але за принципом голубиної дуги все ж є ймовірність, що вона може піти не так і зіткнутись. Це просто неймовірно малоймовірно. Тож удачі дістати пиво з мене.

Однак, навіть якщо клас буде коли-небудь жити в одній системі та одній базі коду, думка, що збільшення числа вручну дає вам нульовий шанс зіткнення, просто означає, що ви не розумієте людей. :)


Якщо "система" торкається класу, тобто змінює клас таким чином, що серіалізація стає несумісною, виникає питання, чи не зміниться також система serialVersionUID. Я не думаю, що шанси менші, що він запам'ятає змінити його, коли він довгий. Я думаю, що навпаки, це справедливо навпаки, якщо число легше запам'ятовується, зміни є вищими, я помічаю, що я випадково його не змінив.
Reto Gmür

2
Це помилково! Коли ви генеруєте serialVersionUID і оголошуєте це значення у своєму вихідному коді, а не 1L або нічого, що ви насправді говорите: я хочу в майбутньому невиявленого зіткнення з невизначеними ефектами, і я не хочу, щоб Java або будь-які люди запобігали цьому . Ява параноїдальний, але слухняний. Люди зазвичай не возиться з великою кількістю. Таким чином, коли клас змінюється, java все ще може де-серіалізувати старі несумісні його версії. MwoaHaHa ...;)
Суперол

1

Ну, serialVersionUID - виняток із правила, що "статичні поля не серіалізуються". ObjectOutputStream кожного разу записує значення serialVersionUID у вихідний потік. ObjectInputStream зчитує його назад, і якщо значення, прочитане з потоку, не узгоджується зі значенням serialVersionUID у поточній версії класу, то воно викидає InvalidClassException. Більше того, якщо в класі офіційно не оголошено serialVersionUID, який підлягає серіалізації, компілятор автоматично додає його зі значенням, згенерованим на основі полів, оголошених у класі.


0

Тому що в багатьох випадках ідентифікатор за замовчуванням не є унікальним. тому ми створюємо ідентифікатор для створення унікальної концепції.


Чи можете ви відредагувати свою відповідь, щоб детальніше розібратися? Це здається коментарем тут. Дякую.
Сірий

0

Щоб додати відповідь на @David Schmitts, я, як правило, завжди використовував стандартний 1L за умовами. Мені довелося лише кілька разів повертатися та змінювати деякі з них, але я це знав, коли вносив зміни та оновлював номер за замовчуванням по одному кожного разу.

У моїй сьогоднішній компанії вони вимагають автоматично генерований номер, тому я використовую це для з'їзду, але я вважаю за краще за замовчуванням. Моя думка, якщо це не конвенція, де ви працюєте, використовуйте за замовчуванням, якщо ви не думаєте, що з якоїсь причини будете постійно змінювати структуру своїх серіалізованих класів.


0

Мета UID версії серіалізації - відстежувати різні версії класу з метою виконання дійсної серіалізації об'єктів.

Ідея полягає у створенні ідентифікатора, унікального для певної версії класу, який потім змінюється, коли до класу додаються нові деталі, наприклад, нове поле, яке вплине на структуру серіалізованого об'єкта.

Просте пояснення:

Ви серіалізуєте дані?

Серіалізація - це в основному запис даних класу у файл / потік / тощо. Десеріалізація - це зчитування цих даних назад до класу.

Ви маєте намір зайнятися виробництвом?

Якщо ви просто тестуєте щось з неважливими / фальшивими даними, тоді не хвилюйтеся про це (якщо ви не тестуєте серіалізацію безпосередньо).

Це перша версія?

Якщо так, встановіть serialVersionUID = 1L.

Це друга, третя і т. П. Версія?

Тепер вам потрібно потурбуватися про serialVersionUID, і слід вивчити його вглиб.

Якщо ви не оновлюєте версію правильно під час оновлення класу, який потрібно писати / читати, ви отримаєте помилку, коли спробуєте прочитати старі дані.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.