Серіалізация в java
Серіалізация в Java
Серіалізация це процес збереження стану об'єкта в електронних даних; десеріалізацію це процес відновлення об'єкта, з цих байт. Java Serialization API надає стандартний механізм для створення Серіалізуемое об'єктів. У цій статті ви побачите як серіалізовать об'єкт, і чому сериализация іноді необхідна. Ви дізнаєтеся про алгоритм сериализации використовуваному в Java і побачите приклад, який ілюструє серіалізований формат об'єкта. В кінці у вас повинно скластися чітке уявлення про те, як працює алгоритм сериализации, а так само яким чином представлені частини об'єкта в серіалізовані вигляді.
Навіщо сериализация потрібна?
У сьогоднішньому світі типове промислове застосування матиме безліч компонентів і буде поширено через різні системи і мережі. В Java все представлено у вигляді об'єктів; Якщо двом компонентам Java необхідно спілкуватися один з одним, то їм необхідний механізм для обміну даними. Є кілька способів реалізувати цей механізм. Перший спосіб це розробити власний протокол і передати об'єкт. Це означає, що одержувач повинен знати протокол, який використовується відправником для відтворення об'єкта, що ускладнює розробку сторонніх компонентів. Отже, повинен бути універсальний і ефективний протокол передачі об'єктів між компонентами. Серіалізация створена для цього, і компоненти Java використовують цей протокол для передачі об'єктів.
Як серіалізовать об'єкт?
Для початку слід переконатися, що клас Серіалізуемое об'єкта реалізує інтерфейс java.io.Serializable як показано в лістингу 1.
class TestSerial implements Serializable public byte version = 100;
public byte count = 0;
>
* This source code was highlighted with Source Code Highlighter.
У лістингу 1 тільки одна річ відрізняється від створення нормального класу, це реалізація інтерфейсу java.io.Serializable. Інтерфейс Serializable це інтерфейс-маркер; в ньому не задекларовано жодного методу. Але каже серіалізуются механізму, що клас може бути серіалізовані.
Тепер у нас є все необхідне для сериализации об'єкта, наступним кроком буде фактична сериализация об'єкта. Вона робиться викликом методу writeObject () класу java.io.ObjectOutputStream. як показано в лістингу 2.
public static void main (String args []) throws IOException FileOutputStream fos = new FileOutputStream ( "temp.out");
ObjectOutputStream oos = new ObjectOutputStream (fos);
TestSerial ts = new TestSerial ();
oos.writeObject (ts);
oos.flush ();
oos.close ();
>
* This source code was highlighted with Source Code Highlighter.
У лістингу 2 показано збереження стану екземпляра TestSerial в файл з ім'ям temp.out
Для відтворення об'єкта з файлу, необхідно застосувати код з лістингу 3.
public static void main (String args []) throws IOException FileInputStream fis = new FileInputStream ( "temp.out");
ObjectInputStream oin = new ObjectInputStream (fis);
TestSerial ts = (TestSerial) oin.readObject ();
System. out .println ( "version =" + ts.version);
>
* This source code was highlighted with Source Code Highlighter.
Відновлення об'єкта відбувається за допомогою виклику методу oin.readObject (). У методі відбувається читання набору байт з файлу і створює точної копії графа оригінального об'єкта. oin.readObject () може прочитати будь-який серіалізовані об'єкт, тому необхідно отриманий об'єкт приводити до конкретного типу.
Виконаний код виведе version = 100 в стандартний висновок.
Формат серіалізовані об'єкта
Як повинен виглядати серіалізовані об'єкт? Згадайте простий код з попереднього розділу, який серіалізуются об'єкт класу TestSerial і записує в temp.out. У лістингу 4 показано вміст файлу temp.out. в шістнадцятковому вигляді.
AC ED 00 05 73 72 00 0A 53 65 72 69 61 6C 54 65
73 74 A0 0C 34 00 FE B1 DD F9 02 00 02 42 00 05
63 6F 75 6E 74 42 00 07 76 65 72 73 69 6F 6E 78
70 00 64
Якщо ви знову подивіться на TestSerial. то побачите, що у нього всього 2 байтових члена. Як показано в лістингу 5.
public byte version = 100;
public byte count = 0;
* This source code was highlighted with Source Code Highlighter.
Розмір байтовой змінної один байт, і отже повний розмір об'єкта (без заголовка) - два байта. Але розмір серіалізовані об'єкта 51 байт. Здивовані? Звідки взялися ці додаткові байти і що вони означають? Вони додані серіалізуются алгоритмом і необхідні для відтворення об'єкта. У наступному абзаці буде докладно описаний цей алгоритм.
Алгоритм сериализации Java
До цього моменту у вас вже має бути достатньо знань, щоб серіалізовать об'єкт. Але як працює цей механізм? Алгоритм сериализации робить такі речі:
- запис метаданих про клас асоційоване з об'єктом
- рекурсивна запис опису суперкласів, до тих пір поки не буде досягнутий java.lang.object
- після закінчення записи метаданих починається запис фактичних даних асоційованих з екземпляром, тільки цього разу починається запис з самого верхнього суперкласу
- рекурсивна запис даних асоційованих з екземпляром починаючи з найнижчого суперкласу
У лістингу 6 прикладів охоплює всі можливі випадки сериализации
class parent implements Serializable int parentVersion = 10;
>
class contain implements Serializable int containVersion = 11;
>
public class SerialTest extends parent implements Serializable int version = 66;
contain con = new contain ();
public int getVersion () return version;
>
public static void main (String args []) throws IOException FileOutputStream fos = new FileOutputStream ( "temp.out");
ObjectOutputStream oos = new ObjectOutputStream (fos);
SerialTest st = new SerialTest ();
oos.writeObject (st);
oos.flush ();
oos.close ();
>
>
* This source code was highlighted with Source Code Highlighter.
У прикладі серіалізуются об'єкт класу SerialTest. який успадковується від parent і містить об'єкт-контейнер класу contain. У лістингу 7 показаний серіалізовані об'єкт.
AC ED 00 05 73 72 00 0A 53 65 72 69 61 6C 54 65
73 74 05 52 81 5A AC 66 02 F6 02 00 02 49 00 07
76 65 72 73 69 6F 6E 4C 00 03 63 6F 6E 74 00 09
4C 63 6F 6E 74 61 69 6E 3B 78 72 00 06 70 61 72
65 6E 74 0E DB D2 BD 85 EE 63 7A 02 00 01 49 00
0D 70 61 72 65 6E 74 56 65 72 73 69 6F 6E 78 70
00 00 00 0A 00 00 00 42 73 72 00 07 63 6F 6E 74
61 69 6E FC BB E6 0E FB CB 60 C7 02 00 01 49 00
0E 63 6F 6E 74 61 69 6E 56 65 72 73 69 6F 6E 78
70 00 00 00 0B
Давайте розглянемо, що собою представляє кожен байт в серіалізовані об'єкті. На початку йде інформація про протокол сериализации:
- AC ED: STREAM_MAGIC. Говорить про те, що використовується протокол серіазізаціі.
- 00 05: STREAM_VERSION. Версія сериализации.
- 0x73: TC_OBJECT. Позначення нового об'єкта.
На першому кроці алгоритм сериализации записує опис класу асоційованого з об'єктом. У прикладі був серіалізовані об'єкт класу SerialTest. отже алгоритм почав записувати опис класу SerailTest.
- 0x72: TC_CLASSDESC. Позначення нового класу.
- 00 0A. Довжина імені класу.
- 53 65 72 69 61 6c 54 65 73 74: SerialTest. ім'я класу.
- 05 52 81 5A AC 66 02 F6: SerialVersionUID. ідентифікатор класу.
- 0x02. Різні прапори. Цей специфічний прапор говорить про те, що об'єкт підтримує сериализацию.
- 00 02. Число полів в класі.
Тепер алгоритм записує поле int version = 66; .
- 0x49. Код типу поля. 49 це «I», яке закріплено за Int.
- 00 07. Довжина імені поля.
- 76 65 72 73 69 6F 6E: version. ім'я поля.
Потім алгоритм записує наступне поле, contain con = new contain () ;. Це об'єкт отже буде записано канонічне JVM позначення цього поля.
- 0x74: TC_STRING. Позначає новий рядок.
- 00 09. Довжина рядка.
- 4C 63 6F 6E 74 61 69 6E 3B: Lcontain ;. Канонічне JVM обозначаеніе.
- 0x78: TC_ENDBLOCKDATA. Кінець опционального блоку даних для об'єкта.
Наступним кроком алгоритму є запис опису класу parent. який є безпосереднім суперкласом для SerialTest.
- 0x72: TC_CLASSDESC. Позначення нового класу.
- 00 06. Довжина імені класу.
- 70 61 72 65 6E 74: parent. ім'я класу
- 0E DB D2 BD 85 EE 63 7A: SerialVersionUID. ідентифікатор класу.
- 0x02. Різні прапори. Цей прапор позначає що клас підтримує сериализацию.
- 00 01. Число полів в класі.
Тепер алгоритм записує опис полів класу parent. клас має одне поле, int parentVersion = 100; .
- 0x49. Код типу поля. 49 позначає «I», яке закріплено за Int.
- 00 0D. Довжина імені поля.
- 70 61 72 65 6E 74 56 65 72 73 69 6F 6E. parentVersion, ім'я поля.
- 0x78: TC_ENDBLOCKDATA. кінець опционального блоку даних для об'єкта.
- 0x70: TC_NULL. позначає те що більше немає суперкласів, тому що ми досягли верху ієрархії класів.
До цього алгоритм сериализации записував опис класів асоційованих з об'єктом і всіх його суперкласів. Тепер будуть записані фактичні дані асоційовані з об'єктом. Спочатку записуються члени класу parent:
- 00 00 00 0A: 10. Значення parentVersion.
Далі подорожуємо у SerialTest
- 00 00 00 42: 66. Значення version.
Наступні кілька байт дуже цікаві. Алгоритму необхідно записати інформацію об'єкті класу contain.
contain con = new contain ();
* This source code was highlighted with Source Code Highlighter.
Алгоритм сериализации ще не записав опис класу contain. Настав момент це зробити.
- 0x73: TC_OBJECT. позначає новий об'єкт.
- 0x72: TC_CLASSDESC. позначає новий клас.
- 00 07. Довжина імені класу.
- 63 6F 6E 74 61 69 6E: contain. ім'я класу.
- FC BB E6 0E FB CB 60 C7: SerialVersionUID. ідентифікатор цього класу.
- 0x02. Різні прапори. Цей прапор позначає що клас підтримує сериализацию.
- 00 01. Число полів в класі.
Алгоритм повинен записати опис єдиного поля класу conatin, int containVersion = 11; .
- 0x49. Код типу поля. 49 позначає «I», яке закріплено за Int.
- 00 0E. Довжина імені поля.
- 63 6F 6E 74 61 69 6E 56 65 72 73 69 6F 6E: containVersion. ім'я поля.
- 0x78: TC_ENDBLOCKDATA. кінець опционального блоку даних для об'єкта.
Далі алгоритм перевіряє, чи має contain батьківський клас. Якщо має, то алгоритм починає запис цього класу; але в нашому випадку суперкласу у contain нету, і алгоритм записує TC_NULL.
В кінці алгоритм записує фактичні дані асоційовані з об'єктом класу conatin.
- 00 00 00 0B: 11. значення containVersion.