Как сделать класс сериализуемым java

Сериализация в Java. Не все так просто

Как сделать класс сериализуемым java

Сериализация (Serialization) — это процесс, который переводит объект в последовательность байтов, по которой затем его можно полностью восстановить. Зачем это нужно? Дело в том, при обычном выполнении программы максимальный срок жизни любого объекта известен — от запуска программы до ее окончания. Сериализация позволяет расширить эти рамки и «дать жизнь» объекту так же между запусками программы.

Дополнительным бонусом ко всему является сохранение кроссплатформенности. Не важно какая у вас операционная система, сериализация переводит объект в поток байтов, который может быть восстановлен на любой ОС. Если вам необходимо передать объект по сети, вы можете сериализовать объект, сохранить его в файл и передать по сети получателю. Он сможет восстановить полученный объект. Так же сериализация позволяет осуществлять удаленный вызов методов (Java RMI), которые находятся на разных машинах с, возможно, разными операционными системами, и работать с ними так, словно они находятся на машине вызывающего java-процесса.

Реализовать механизм сериализации довольно просто. Необходимо, чтобы ваш класс реализовывал интерфейс Serializable. Это интерфейс — идентификатор, который не имеет методов, но он указывает jvm, что объекты этого класса могут быть сериализованы. Так как механизм сериализации связан с базовой системой ввода/вывода и переводит объект в поток байтов, для его выполнения необходимо создать выходной поток OutputStream, упаковать его в ObjectOutputStream и вызвать метод writeObject(). Для восстановления объекта нужно упаковать InputStream в ObjectInputStream и вызвать метод readObject().

В процессе сериализации вместе с сериализуемым объектом сохраняется его граф объектов. Т.е. все связанные с этим объекто, объекты других классов так же будут сериализованы вместе с ним.

Рассмотри пример сериализации объекта класса Person.

Вывод:

В данном примере класс Home создан для того чтобы продемонстрировать, что при сериализации объекта Person, с ним сериализуется и граф его объектов. Класс Home так же должен реализовывать интерфейс Serializable, иначе случится исключение java.io.NotSerializableException. Так же в примере описана сериализация с помощью класса ByteArrayOutputStream.

Из результатов выполнения программы можно сделать интересный вывод: при восстановлении объектов, у которых до сериализации была ссылка на один и тот же объект, этот объект будет восстановлен только один раз. Это видно по одинаковым ссылкам в объектах после восстановления:

Однако, так же видно, что при выполнении записи двумя потоками вывода (у нас это ObjectInputStream и ByteArrayOutputStream), объект home будет создан заново, несмотря на то, что он уже был создан до этого в одном из потоков. Мы видим это по разным адресам объектов home, полученных в двух потоках. Получается, что если выполнить сериализацию одним выходным поток, затем восстановить объект, то у нас есть гарантия восстановления полной сети объектов без лишних дубликатов. Конечно, в ходе выполнения программы состояние объектов может измениться, но это на совести программиста.

Проблема

Из примера так же видно, что при восстановлении объекта может возникнуть исключение ClassNotFoundException. С чем это связано? Дело в том, что мы легко можем сериализовать объект класса Person в файл, передать его по сети нашему товарищу, который может восстановить объект другим приложением, в котором класса Person попросту нет.

Своя сериализация. Как сделать?

Что делать, если вы хотите управлять сериализацией сами? Например, ваш объект хранит в себе логин и пароль пользователей. Вам необходимо сериализовать его для дальнейшей передачи его по сети. Передавать пароль в таком случае крайне ненадежно. Как решить эту задачу? Существует два способа. Первый, использовать ключевое слово transient. Второй, вместо реализации интереса Serializable использовать его расширение — интерфейс Externalizable. Рассмотрим примеры работы первого и второго способа для их сравнения.

Первый способ — Сериализация с использованием transient

Вывод:

Второй способ — Сериализация с реализацией интерфейса Externalizable

Вывод:

Первое отличие двух вариантов, которое бросается в глаза это размер кода. При реализации интерфейса Externalizable нам необходимо переопределить два метода: writeExternal() и readExternal(). В методе writeExternal() мы указываем какие поля будут сериализованы и как, в readExternal() как их прочитать. При использовании слова transient мы явно указываем, какое поле или поля не нужно сериализовывать. Так же заметим, что во втором способе мы явно создали конструктор по умолчанию, причем публичный. Зачем это сделано? Давайте попробуем запустить код без этого конструктора. И посмотрим на вывод:

Мы получили исключение java.io.InvalidClassException. С чем это связано? Если пройти по стек-трейсу можно выяснить, что в конструкторе класса ObjectStreamClass есть строчки:

Для интерфейса Externalizable будет вызван метод получения конструктора getExternalizableConstructor(), внутри которого мы через Reflection попробуем получить конструктор по умолчанию класса, для которого мы восстанавливаем объект. Если нам не удается его найти, или он не public, то мы получаем исключение. Обойти эту ситуацию можно следующим образом: не создавать явно никакого конструктора в классе и заполнять поля с помощью сеттеров и получать значение геттерами. Тогда при компиляции класса будет создан конструктор по умолчанию, который будет доступен для getExternalizableConstructor(). Для Serializable метод getSerializableConstructor() получает конструктор класса Object и от него ищет нужный класс, если не найдет, то получим исключение ClassNotFoundException. Выходит, что ключевое различие между Serializable и Externalizable в том, что первому не нужен конструктор для создания восстановления объекта. Он просто полностью восстановится из байтов. Для второго при восстановлении сначала будет создан объект с помощью конструктора в точке объявления, а затем в него будут записаны значения его полей из байтов, полученных при сериализации. Лично мне больше нравится первый способ, он гораздо проще. Причем, даже если нам нужно все таки задать поведение сериализации, мы можем не использовать Externalizable, а так же реализовать Serializable, добавив (не переопределив) в него методы writeObject() и readObject(). Но для того, чтобы они «работали» нужно точно соблюсти их сигнатуру.

Вывод:

Внутри наших добавленных методов вызываются defaultWriteObject() и defaultReadObject(). Они отвечают за сериализацию по умолчанию, как если бы она работала без добавленных нами методов.

На самом деле это только верхушка айсберга, если продолжить углубляться в механизм сериализации, то с высокой доли вероятности, можно отыскать еще нюансы, найдя которые мы скажем: «Сериализация… не все так просто».

Источник

Сериализация в Java

Зачем сериализация нужна?

В сегодняшнем мире типичное промышленное приложение будет иметь множество компонентов и будет распространено через различные системы и сети. В Java всё представлено в виде объектов; Если двум компонентам Java необходимо общаться друг с другом, то им необходим механизм для обмена данными. Есть несколько способов реализовать этот механизм. Первый способ это разработать собственный протокол и передать объект. Это означает, что получатель должен знать протокол, используемый отправителем для воссоздания объекта, что усложняет разработку сторонних компонентов. Следовательно, должен быть универсальный и эффективный протокол передачи объектов между компонентами. Сериализация создана для этого, и компоненты Java используют этот протокол для передачи объектов.

Рисунок 1 демонстрирует высоко-уровневое представление клиент-серверной коммуникации, где объект передаётся с клиента на сервер посредством сериализации.
Как сделать класс сериализуемым java
Рисунок 1.

Как сериализовать объект?

Для начала следует убедиться, что класс сериализуемого объекта реализует интерфейс java.io.Serializable как показано в листинге 1.

class TestSerial implements Serializable <
public byte version = 100;
public byte count = 0;
>

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();
>

В листинге 2 показано сохранение состояния экземпляра TestSerial в файл с именем temp.out

Для воссоздания объекта из файла, необходимо применить код из листинга 3.

Формат сериализованного объекта

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

public byte version = 100;
public byte count = 0;

Размер байтовой переменной один байт, и следовательно полный размер объекта (без заголовка) — два байта. Но размер сериализованного объекта 51 байт. Удивлены? Откуда взялись эти дополнительные байты и что они обозначают? Они добавлены сериализующим алгоритмом и необходимы для воссоздания объекта. В следующем абзаце будет подробно описан этот алгоритм.

Алгоритм сериализации Java

К этому моменту у вас уже должно быть достаточно знаний, чтобы сериализовать объект. Но как работает этот механизм? Алгоритм сериализации делает следующие вещи:

В листинге 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();
>
>

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

На рисунке 2 показан сценарий алгоритма сериализации.
Как сделать класс сериализуемым java
Рисунок 2.

Заключение

В этой статье вы увидели как сериализовать объект, и узнали как работает алгоритм сериализации. Я надеюсь эта статья помогла вам лучше понять что происходит, когда вы сериализуете объект.

Об авторе

Sathiskumar Palaniappan имеет более чем 4-х летний опыт работы в IT-индестрии, и работает с Java технологиями более 3 лет. На данный момент он работает system software engineer в Java Technology Center, IBM Labs. Также имеет опыт работы в телекоммуникационной индустрии.

Источник

32. Java — Сериализация

Java предоставляет механизм, называемый сериализацией объектов, в котором объект может быть представлен в виде последовательности байтов, которая включает в себя данные об объекте, а также информацию о типе объекта и типах данных, хранящихся в объекте.

Содержание

После того, как сериализованный объект был записан в файл, он может быть прочтен из файла и десериализован, то есть информацию о типе и байты, которые представляют объект и его данные, можно использовать для воссоздания объекта в памяти.

Больше всего впечатляет то, что весь процесс независим от JVM (виртуальной машины Java), то есть объект может быть сериализован на одной платформе и десериализован на совершенно другой платформе.

Класс ObjectOutputStream содержит много методов записи для осуществления записи различных типов данных, но среди них в особенности выделяется один метод:

Вышеуказанный метод осуществляет сериализацию Объекта и отправляет его в поток выходных данных. Аналогично, класс ObjectInputStream содержит следующий метод осуществления десериализации объекта:

Чтобы продемонстрировать, каким образом сериализация работает в Java, я собираюсь использовать класс Employee (сотрудник), который обсуждался в начале книги. Предположим, что мы располагаем следующим классом Employee, который внедряет сериализируемый интерфейс.

Пример

Обратите внимание, что для успешного выполнения сериализации класса должны быть выполнены два условия:

Сериализация объекта

Класс ObjectOutputStream используется для выполнения сериализации объекта. Следующая программа SerializeDemo создает экземпляр объекта Employee и сериализует его в файл.

По завершению выполнения программы создается файл с именем employee.ser. Программа не генерирует никаких выходных данных, но изучает код и пытается определить, что совершает программа.

Примечание: в Java при сериализации объекта в файл установленным архитектурным требованием Java является присвоение файлу расширения .ser.

Пример

Десериализация объекта

Следующая программа DeserializeDemo выполняет в Java десериализацию объекта Employee, созданного в программе SerializeDemo. Изучите программу и попытайтесь определить ее выводимые данные.

Пример

Это даст следующий результат:

Следующие важные моменты, которые следует отметить, представлены ниже:

Источник

Форматы сериализации в Java

Human-readable («человеко-читаемый») формат. Это очевидное преимущество, если твой конечный пользователь — человек. К примеру, на твоем сервере хранится база данных с расписанием авиаперелетов. Клиент-человек запрашивает данные из этой базы с помощью веб-приложения, сидя дома за компьютером. Поскольку тебе нужно предоставить данные в формате, который он сможет понять, JSON будет отличным решением.

Простота. Можно сказать — элементарность 🙂 Выше мы привели пример двух JSON-файлов. И даже если ты вообще не слышал о существовании JavaScript (и уж тем более о его объектах), ты легко поймешь, что за объекты там описаны.
Вся документация JSON — это одна веб-страница с парой картинок.

Широкая распространенность. JavaScript — доминирующий язык фронтенда, и он диктует свои условия. Использование JSON — необходимость. Поэтому огромное число веб-сервисов используют JSON в качестве формата для обмена данными. Каждая современная IDE поддерживает JSON-формат (в том числе Intellij IDEA). Для работы с JSON написана куча библиотек для всех возможных языков программирования.

Например, ты уже работал с библиотекой Jackson в лекции, где мы учились сериализовывать Java-объекты в JSON. Но помимо Jackson есть, например, GSON — очень удобная библиотека от Google.

Human-readable. Опять же, даже увидев yaml-файл без описания, ты легко поймешь, какие объекты там описаны. YAML насколько хорошо читается человеком, что главная страница yaml.org — это обычный yaml-файл 🙂

Компактность. Структура файла формируется за счет пробелов: нет необходимости использовать скобки или кавычки.

Поддержка структур данных, «родных» для языков программирования. Огромное преимущество YAML перед JSON и многими другими форматами заключается в том, что он поддерживает разные структуры данных. В их числе:

!!map
Неупорядоченный набор пар ключ:значение без возможности дубликатов;

!!omap
Упорядоченная последовательность пар ключ:значение без возможности дубликатов;

!!pairs:
Упорядоченная последовательность пар ключ:значение с возможностью дубликатов;

Некоторые из этих структур знакомы тебе по Java! 🙂 За счет этой фичи в формат YAML можно сериализовать разные структуры данных из языков программирования.

Возможность использования anchor и alias

Перевод слов «anchor» и «alias» — «якорь» и «псевдоним». В принципе, он довольно точно описывает суть этих терминов в YAML.

Допустим, у нас есть файл с описанием книг Льва Толстого. Чтобы не писать имя автора каждый раз вручную, мы просто создадим якорь «leo» и будем ссылаться на него с помощью алиаса, когда нам это будет нужно:

Когда мы будем считывать этот файл каким-то парсером, на месте нашего алиаса в нужных местах будет подставляться значение «Leo Tolstoy».

Источник

Сериализация и десериализация в Java с примером

Сериализация — это механизм преобразования состояния объекта в поток байтов. Десериализация — это обратный процесс, в котором поток байтов используется для воссоздания фактического объекта Java в памяти. Этот механизм используется для сохранения объекта.

Как сделать класс сериализуемым java

Созданный поток байтов не зависит от платформы. Таким образом, объект, сериализованный на одной платформе, может быть десериализован на другой платформе.

Класс ObjectInputStream содержит метод readObject () для десериализации объекта.

Преимущества сериализации
1. Сохранить / сохранить состояние объекта.
2. Для перемещения объекта по сети.

Как сделать класс сериализуемым java

Очки для запоминания
1. Если родительский класс реализовал интерфейс Serializable, тогда дочерний класс не должен реализовывать его, но, наоборот, это не так.
2. Только не статические члены данных сохраняются в процессе сериализации.
3. Элементы статических данных и переходные данные не сохраняются с помощью процесса сериализации. Так что, если вы не хотите сохранять значение элемента не статических данных, сделайте его переходным.
4. Конструктор объекта никогда не вызывается при десериализации объекта.
5. Связанные объекты должны реализовывать интерфейс Serializable.
Пример :

Если сериализуемый класс явно не объявляет serialVersionUID, тогда среда выполнения сериализации вычислит класс по умолчанию для этого класса на основе различных аспектов класса, как описано в Спецификации сериализации объектов Java. Однако настоятельно рекомендуется, чтобы все сериализуемые классы явно объявляли значение serialVersionUID, поскольку его вычисления очень чувствительны к деталям класса, которые могут варьироваться в зависимости от реализаций компилятора, любое изменение в классе или использование другого идентификатора может повлиять на сериализованные данные.

Также рекомендуется использовать закрытый модификатор для UID, так как он не используется как унаследованный член.

serialver
Серийный сервер — это инструмент, который поставляется с JDK. Он используется для получения номера serialVersionUID для классов Java.
Вы можете запустить следующую команду, чтобы получить serialVersionUID

serialver [-classpath classpath] [-show] [имя класса…]

Как сделать класс сериализуемым java

Пример 1:

// Java-код для сериализации и десериализации
// объекта Java

Источник

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *