Питання Що таке serialVersionUID і чому я повинен його використовувати?


Eclipse видає попередження, коли a serialVersionUID відсутня.

Серіалізується клас Foo не оголошує статичний фінал   Поле типу serialVersionUID довге

Що serialVersionUID і чому це важливо? Будь-ласка, покажіть приклад, де його немає serialVersionUID викликає проблему.


2489
2017-11-12 23:24


походження


Простий приклад mkyong.com/java-best-practices/understand-the-serialversionuid - Gopinagh.R


Відповіді:


Документи для java.io.Serializable мабуть, приблизно такий же хороший пояснення, як ви отримаєте:

Тривалість серіалізації приєднується   з кожним serializable клас версією   номер, називається serialVersionUID,   який використовується під час десеріалізації   перевірити, що відправник і одержувач   з серійного об'єкта завантажені   класів для цього об'єкта, які є   сумісний щодо   серіалізація Якщо є одержувач   завантажив клас для об'єкта, який має   інший serialVersionUID, ніж це   відповідного класу відправника,   то десериалізація призведе до а    InvalidClassException. Серійно   клас може заявити свою власну   serialVersionUID явно на   декларування поля з назвою   "serialVersionUID"це повинно бути   статичний, фінальний та тип long:

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

Якщо   serializable клас не явно   оголосити serialVersionUID, потім   Серійний час виконання буде розрахувати a   Стандартне значення serialVersionUID для   цей клас заснований на різних аспектах   клас, як описано в   Java (TM) серіалізація об'єктів   Специфікація. Однак це так сильно   рекомендовано що все серіалізується   класи явним чином оголошують   значення serialVersionUID, починаючи з   стандартний обчислення serialVersionUID   дуже чутливий до деталей класу   що може змінюватися залежно від компілятора   реалізацій, і, таким чином, може бути результатом   в несподівано InvalidClassExceptions   під час десеріалізації. Тому, до   гарантувати послідовність   значення serialVersionUID через   інший компілятор Java   реалізацій, серіалізується клас   повинен оголосити явний   значення serialVersionUID. Це також   настійно рекомендує це явне   декларації serialVersionUID використовують   приватний модифікатор, де це можливо, оскільки   такі заяви застосовуються лише до   негайно оголосити   клас - serialVersionUID поля не є   корисні як успадковані учасники.


1911
2017-11-12 23:30



Отже, що ви, власне кажуть, в тому, що якщо користувач не зрозумів усіх вищезгаданих матеріалів, то сказав користувач, не турбуючись про серіалізацію? Я вірю, що ви відповіли "як?" а не пояснення "чому?". Я, по-перше, не розумію, чому я турбуюся з SerializableVersionUID. - Ziggy
Чому це у другому абзаці: якщо ви не чітко визначаєте serialVersionUID, значення автоматично генерується - але це крихке, оскільки це залежить від реалізації компілятора. - Jon Skeet
І чому Eclipse каже, що мені потрібен "приватний статичний остаточний довгий serialVersionUID = 1L;" коли я продовжую клас винятку? - JohnMerlino
@ Джон Мерліно: Я б не очікував, що він вам скаже треба один - але це може бути пропонуючи один з яких допоможе вам серіалізувати винятки правильно. Якщо ви не збираєтеся серіалізувати їх, вам дійсно не потрібна постійна. - Jon Skeet
@ Джон Мерліно, щоб відповісти на те, чому у вас питання: Виняток реалізує Серійний і Eclipse попереджає, що ви не встановили serialVersionUID, що було б непоганою ідеєю (якщо ви не захочете серіалізувати клас), щоб уникнути проблем, які очолює JonSkeet. - zpon


Якщо ви робите серіалізацію просто тому, що ви повинні серіалізувати для реалізації (хто турбується, якщо ви серіалізуєте для HTTPSession, наприклад ... якщо воно зберігається чи ні, ви, напевно, не дбаєте про де-серіалізацію об'єкта форми) , то ви можете проігнорувати це.

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

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


415
2017-11-13 04:24



Я б сказав, що якщо ви не використовуєте серіалізацію для постійного зберігання, ви повинні використовувати @SuppressWarnings, а не додавати значення. Це заважає класу менше, і він зберігає абітурітетний механізм serialVersionUID, щоб захистити вас від несумісних змін. - Tom Anderson
Я не бачу, як додавання одного рядка (анотація @SuppressWarnings) на відміну від іншої рядки (serializable id) "захарає клас менше". І якщо ви не використовуєте серіалізацію для постійного зберігання, чому б ви не просто використовувати "1"? Ви нічого не хотіли б про автогенерований ідентифікатор у цьому випадку. - MetroidFan2002
@ MetroidFan2002: Я думаю, що точка Тома Андерсона serialVersionUID захист від несумісних змін є дійсним. Використовуючи @SuppressWarnings документує намір краще, якщо ви не бажаєте використовувати клас для постійного зберігання. - AbdullahC
"Ви повинні збільшити його, якщо поточна версія вашого класу не сумісна з попередньою версією:" Спочатку слід вивчити супровід версій об'єктів Serialization (а), щоб переконатися, що клас дійсно є несумісним із серіалізацією. які за специфікацією досить важко досягти; (b) спробувати схему, таку як способи користувацького читання / writeObject (), readResolve / writeReplace (), декларації serializableFields тощо, щоб переконатися, що потік залишається сумісним. Зміна фактичного serialVersionUID є останньою інстанцією, порадою відчаю. - user207421
@EJP приріст serialVersionUID входить до зображення, коли початковий автор класу ввів явно. Я б сказав, JVM серійний ідентифікатор, має бути добре. це найкраще відповісти що я бачив у серіалізації. - overexchange


Я не можу пропустити цю можливість, щоб закріпити книгу Джоша Блоха Ефективна Java (2-е видання). Глава 11 є невід'ємним ресурсом для серіалізації Java.

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

Якщо ви ігноруєте їх зараз і знайдіть пізніше, що вам потрібно буде змінити клас, але зберегти сумісність з старою версією класу, ви можете використовувати інструмент JDK serialver щоб створити serialVersionUID на старий клас, і явно встановити, що в новому класі. (Залежно від ваших змін вам може знадобитися також застосувати користувальницьку серіалізацію шляхом додавання writeObject і readObject методи - див Serializable javadoc або вищезгаданий розділ 11.)


264
2017-11-12 23:37



Тож, хтось може турбуватися з SerializableVersionUID, якщо хтось турбується про сумісність w / старих версій класу? - Ziggy
Так, у випадку, якщо нова версія змінює будь-який загальнодоступний користувач для захищеного, стандартний SerializableVersionUID буде іншим і підніме InvalidClassExceptions. - Chander Shivdasani
клас Ім'я, виконані інтерфейси, всі загальнодоступні та захищені методи, ВСІ екземпляри змінних. - Achow
Варто зазначити, що Джошуа Блох порадив цьому кожен Serializable класу варто вказати серійну версію uid. Цитата з глави 11: Незалежно від того, яку серійну форму ви виберете, оголосити явну серійну версію UID у кожному серійному класі, який ви пишете. Це усуває серійну версію UID як потенційного джерела несумісності (пункт 74). Існує також невелика продуктивність вигоди. Якщо не надано серійну версію UID, потрібна дорога обчислення, щоб створити його під час виконання. - Ashutosh Jindal


Ви можете сказати Eclipse, щоб ігнорувати ці попередження про serialVersionUID:

Вікно> Налаштування> Java> Компілятор> Помилки та попередження> Проблеми з можливим програмуванням

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

  • Проблеми потенційного програмування: Можливе випадкове логічне призначення
  • Потенційні проблеми програмування: доступ до нульового вказівника
  • Не потрібний код: локальну змінну ніколи не читають
  • Необхідний код: резервна нульова перевірка
  • Ненужний код: ненужна передача або "instanceof"

і багато іншого.


124
2017-11-13 01:17



upvote але тільки тому, що оригінальний плакат не серіалізує що-небудь. Якщо плакат сказав: "Я серіалізую цю справу і ...", то ви отримаєте голос замість: P - John Gardner
Правда - я вважав, що запитувач просто не хотів бути попереджений - matt b
@ Гарднер -> погодився! Але запитач також хоче знати, чому він може не захотіти бути попереджений. - Ziggy
Опитувач, очевидно, піклується про те, чому повинна бути UID. Тому, просто кажучи йому ігнорувати попередження, слід зменшити його. - cinqS


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

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

Вбудований механізм де-серіалізаціі (in.defaultReadObject()) відмовиться від серіалізації зі старих версій даних. Але якщо ви хочете, ви можете визначити свій власний readObject ()-функція, яка може переглядати старі дані. Цей спеціальний код може перевірити serialVersionUID щоб дізнатися, яка версія є даними, і вирішити, як її де-серіалізувати. Ця методика версії корисна, якщо ви зберігаєте серійні дані, які зберігають кілька версій вашого коду.

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

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

Якщо ви забули оновити поле, ви можете закінчити дві різні версії класу з різною структурою, але з однаковими serialVersionUID. Якщо це станеться, механізм за замовчуванням (in.defaultReadObject()) не виявить ніякої різниці, а також спробуйте де-серіалізувати несумісні дані. Тепер ви можете закінчити з загадкою помилки виконання або безшумної помилки (нульові поля). Ці типи помилок можуть бути важко знайти.

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

Але існує зворотний бік автоматично створених ідентифікаційних стратегій. А саме, що створені ідентифікатори для одного і того ж класу можуть відрізнятися між компіляторами (як згадував Джон Скіт вище). Отже, якщо ви передаєте серіалізовані дані між кодом, складеним з різними компіляторами, в будь-якому випадку рекомендується підтримувати ідентифікатори вручну.

І якщо ви зворотні, сумісні з вашими даними, як у згаданому вище випадку, ви також, можливо, хочете зберегти ідентифікатор самостійно. Це, щоб отримати зрозумілі ідентифікатори та більше контролювати, коли і як вони змінюються.


96
2017-10-03 06:05



Додавання або видалення неперервних полів не робить несумісні серіалізацію класу. Отже, немає підстав для того, щоб "натикатися" на такі зміни. - user207421
@ EJP: а? Додавання даних однозначно змінює дані про серіалізацію в моєму світі. - Alexander Torstling
@AlexanderTorstling Читайте, що я написав. Я не сказав, що це не "змінює дані серіалізації". Я сказав, що "не робить серіалізацію класу несумісною". Це не одне й те саме. Вам потрібно прочитати версію розділу специфікації Serialization Object. - user207421
@ EJP: я розумію, що додавання неперехідного поля не обов'язково означає, що ви робите серіалізацію несумісним, але це структурна зміна, яка змінює серійні дані, і ви, як правило, хочете вершити версію при цьому, якщо ви обробляти зворотну сумісність, яку я також пояснив пізніше у повідомленні. Яка ваша точка точно? - Alexander Torstling
Моя точка залишається саме тим, що я сказав. Додавання або видалення неперехідних полів не робить клас Serialization-несумісним. Тому вам не потрібно вдарити serialVersionUID кожен раз, коли ви це робите. - user207421


Що таке? serialVersionUID і чому я повинен його використовувати?

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

Визначення одного дає більший контроль, хоча JVM генерує його, якщо ви не вкажете. Значення, створене може відрізнятися між різними компіляторами. Крім того, іноді ви просто хочете чомусь заборонити десеріалізацію старих серійних об'єктів [backward incompatibility], і в цьому випадку вам просто доведеться змінити serialVersionUID.

The javadocs for Serializable казати:

типовий випадок serialVersionUID дуже чутливий до класу   деталі, які можуть відрізнятися залежно від реалізації компілятора, і може   таким чином, призводить до несподіваного InvalidClassExceptionS під час   десериализация

Тому ви повинні оголосити serialVersionUID, оскільки це дає нам більший контроль.

Ця стаття має деякі хороші моменти на цю тему.


62
2017-10-17 04:28



@Vinothbabu але serialVersionUID статичний, тому статичні змінні не можуть бути серіалізовані. тоді, як приходить jvm перевірить версію, не знаючи, яка версія десеріалізуючого об'єкта - Kranthi Sama
Одна річ, не згадана в цій відповіді, полягає в тому, що ви можете спричинити непередбачені наслідки сліпо, в тому числі serialVersionUID не знаючи чому. Зауваження Тома Андерсона про відповідь MetroidFan2002 адресовано таким чином: "Я б сказав, що якщо ви не використовуєте серіалізацію для постійного зберігання, вам слід скористатися @SuppressWarnings, а не додавати значення. Це зачіпає клас менш, і воно зберігає можливості механізм serialVersionUID, щоб захистити вас від несумісних змін ". - Kirby
serialVersionUID є ні "унікальний ідентифікатор для кожного класу". Повне ім'я класу це. Це версія індикатор - user207421


Оригінальне запитання "чому це важливо" і "приклад", де це Serial Version ID було б корисно. Ну, я знайшов її.

Скажімо, ви створили a Car клас, проінтернаціонізувати його і записати його до потоку об'єктів. Плоский об'єкт автомобіля на деякий час сидить у файловій системі. Між тим, якщо Car клас змінено шляхом додавання нового поля. Пізніше, коли ви намагаєтеся прочитати (наприклад, десеріалізувати), сплюснуте Car об'єкт, ви отримаєте java.io.InvalidClassException - оскільки всі класи serializable автоматично дають унікальний ідентифікатор. Це виняток викидається, коли ідентифікатор класу не дорівнює ідентифікатору сплощеного об'єкта. Якщо ви дійсно думаєте про це, виняток викидається через додавання нового поля. Ви можете уникнути такого виключення, керуючи версією самостійно, оголосивши явний serialVersionUID. Існує також невелика продуктивність вигоди в явному оголошенні вашої serialVersionUID(тому що не потрібно розраховувати). Таким чином, найкраще додавати власний serialVersionUID до ваших класів Serializable, як тільки ви їх створюєте, як показано нижче:

public class Car {
static final long serialVersionUID = 1L; //assign a long value
}

42
2018-04-07 10:43



І слід призначити випадкове довге число, а не 1L, для кожного UID. - abbas
@ Аббас "Треба" робити це чому? Будь ласка, поясніть, яка різниця вона робить. - user207421
Як зазначається в назві, це представляє версію класу, який використовувався для серіалізації об'єкта. Якщо ви кожного разу присвоїте йому однакову кількість, ви не зможете знайти правильний клас для де-серіалізації. Тому, коли ви вносите зміни в клас, краще також змінити версію. - abbas
@Abbas, це намір не збігається з використанням нарощування натуральних чисел від 1 і так далі. - Vadzim
@BillK, я думав, що перевірка на серіалізацію пов'язана з парою класів і serialVersionUID. Отже, різні схеми нумерації різних класів і бібліотек ніяк не можуть втручатися. Або ви мали на увазі бібліотеки, що генерують код? - Vadzim


Якщо ви отримаєте це попередження в класі, ви ніколи не думаєте про серіалізацію і що ви не заявили про себе implements Serializable, це часто тому, що ви успадкували від суперкласу, який реалізує Serializable. Часто тоді це було б краще делегувати на такий об'єкт, а не використовувати спадщину.

Так, замість

public class MyExample extends ArrayList<String> {

    public MyExample() {
        super();
    }
    ...
}

робити

public class MyExample {
    private List<String> myList;

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

і в відповідних методах виклику myList.foo() замість this.foo() (або super.foo()) (Це не підходить у всіх випадках, але все одно досить часто.)

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

Один із випадків, коли попередження (або serialVersionUID) неминуче відбувається, коли ви продовжуєте роботу з AbstractAction, як правило, в анонімному класі, лише додавши actionPerformed-method. Я думаю, що в цьому випадку не повинно бути попередження (оскільки ви, як правило, не можете надійно серіалізувати та дезеріалізувати такі анонімні класи у різних версіях вашого класу), але я не знаю, як компілятор може це визнати.


33
2018-02-03 00:32



Я думаю, ви маєте рацію, що композиція над непридатністю має сенс, особливо коли ви обговорюєте такі класи, як ArrayList. Однак багато рамок вимагають, щоб люди поширювалися з абстрактних суперкласів, які можуть бути серійними (наприклад, класом ActionForm Struts 1.2 або ExxtensionFunctionDefinition Саксоном), в цьому випадку це рішення неможливе. Я думаю, ви правильно, було б добре, якщо в деяких випадках попередження було проігноровано (наприклад, якщо ви поширювалися з абстрактного серійного класу) - piepera
Звичайно, якщо ви додаєте клас як член, а не наслідує його, вам слід написати метод обгортки для КОЖНОГО методу класу-члена, який ви хотіли використати, що зробить це нездійсненним у великій кількості ситуацій .. якщо Java не має функції, подібні до Perl's __AUTOLOAD, про яку я не знаю. - M_M
@M_M: Коли ви делегуєте багато методів у ваш обтічний об'єкт, звичайно, було б недоцільно використовувати делегування. Але я вважаю, що ця справа є ознакою помилки дизайну - користувачам вашого класу (наприклад, "MainGui") не потрібно викликати багато методів загорнутого об'єкта (наприклад, JFrame). - Paŭlo Ebermann
Що мені не подобається в делегування, є необхідність посилатися на делегата. І кожне посилання означає більше пам'яті. Виправляти мене, якщо я помиляюся. Якщо мені потрібен CustomizedArrayList з 100 об'єктів, це не має великого значення, але якщо мені потрібні сотні CustomizdeArrayLists з декількох об'єктів, то використання пам'яті значно збільшується. - WVrock
Throwable може бути серійним, і тільки Throwable може бути кинутим, тому неможливо визначити виключення, яке не можна серіалізувати. Делегація не можлива. - Phil