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

Добавил пользователь Евгений Кузнецов
Обновлено: 04.10.2024

Много раз встречал эту "сериализацию" на разных ресурсах, часто связано с JSON.

Объясните, пожалуйста, простыми словами, что такое сериализация, где и зачем ее применяют ?

Сериализация — это преобразование объекта или дерева объектов в какой-либо формат с тем, чтобы потом эти объекты можно было восстановить из этого формата. Используется, например, для сохранения состояния программы (то есть, некоторых её объектов) между запусками. Или для передачи данных между различными экземплярами программы (или различными программами), например, по сети.

Главная идея состоит в том, что сериализованный формат — набор байт или строка, которую можно легко сохранить на диск или передать другому процессу или, например, по сети, в отличие от самого объекта. А значит, задача сохранения объекта/группы объектов при этом сводится к простой задаче сохранения набора байт или строки.

JSON — один из популярных форматов для сериализации, он текстовый, легковесный и легко читается человеком.

Пример: если у вас есть класс

Объект этого класса в сериализованной форме может иметь вид

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

Java I / O понимание (шесть) сериализация и десериализация объектов

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

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

Почему существует сериализация

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

Поскольку RMI является основой технологии JAVA EE, все распределенные приложения требуют кроссплатформенности и кросс-сети. Поэтому сериализация является основой JAVA EE, Обычно рекомендуется сериализовать каждый класс JavaBean, созданный программой.

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

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

Вот несколько принципов, давайте посмотрим:

1. Serializable - помеченный интерфейс, в нем не определены методы или поля, которые используются только для обозначения семантики сериализуемого.

2. Статические переменные и методы-члены не сериализуемы.

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

4. Переменные, объявленные как переходные, не сохраняются инструментом сериализации, а статические переменные не сохраняются.

Сначала давайте рассмотрим сериализацию объекта и его сохранение в файле:


1. После сериализации объекта записывается двоичный файл. Нормально открывать все искаженные символы. Однако мы по-прежнему видим, что созданный нами объект сохраняется в файле через искаженные символы.

2. Объект Person реализует интерфейс Serializable.Этот интерфейс не имеет методов, которые должны быть реализованы, только интерфейс пометки, указывающий, что объекты этого класса могут быть сериализованы.


3. В этой программе мы вызываем метод writeObject () объекта ObjectOutputStream для вывода сериализуемого объекта. Этот объект также предоставляет методы для вывода основных типов.

writeFloat(float val)

Во-вторых, давайте посмотрим на процесс десериализации объектов из файла:


1. Разница между этим местом и предыдущим состоит в том, что он предоставляет метод writeObject и метод readObject в классе Person и обеспечивает конкретную реализацию.

2. В процессе вызова метода writeObject в ObjectOutputStream должен быть вызван метод writeObject класса Person, поскольку журнал строки 20 в коде выводится на консоль.

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

4. Здесь, поскольку мы предоставляем эти два метода в классе для сериализации, они вызываются. Если нет, я думаю, что два метода, предоставляемые ObjectOutputStream / ObjectInputStream, будут вызываться по умолчанию.


Проблема сериализации

1. Статические переменные не будут сериализованы.

2. При сериализации подклассов:

Если родительский класс не реализует интерфейс Serializable и не предоставляет конструктор по умолчанию, сериализация подкласса пойдет не так;

Если родительский класс не реализует интерфейс Serializable и предоставляет конструктор по умолчанию, дочерний класс может быть сериализован, а переменные-члены родительского класса не будут сериализованы.

Если родительский класс реализует интерфейс Serializable, родительский и дочерний классы могут быть сериализованы.

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


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

Чтобы сделать сериализуемый объект Java мы реализуем интерфейс java.io.Serializable .
Класс ObjectOutputStream содержит метод writeObject () для сериализации объекта.

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

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


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

SerialVersionUID
Среда выполнения сериализации связывает номер версии с каждым классом Serializable, который называется SerialVersionUID, который используется во время десериализации для проверки того, что отправитель и получатель сериализованного объекта загрузили классы для этого объекта, которые совместимы в отношении сериализации. Если получатель загрузил класс для объекта, который имеет другой UID, чем у соответствующего класса отправителя, десериализация приведет к исключению InvalidClassException . Сериализуемый класс может объявить свой собственный UID явно, объявив имя поля.
Он должен быть статическим, окончательным и иметь тип long.
т. е. ЛЮБОЙ ДОСТУП-МОДИФИКАТОР static final long serialVersionUID = 42L;

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

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

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

Класс ObjectInputStream реализует интерфейс java . io . ObjectInput , а класс ObjectOutputStream реализует интерфейс java . io . ObjectOutput , в который описаны основные методы для чтения и записи объектов.

Чтение объекта из ObjectInputStream осуществляется с помощью метода

Запись объекта в ObjectOutputStream производится с помощью метода:

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

Только классы, реализующие интерфейсы java . io . Serializable или java . io . Externalizable , могут быть считаны из потока и записаны в поток.

Метод readObject возвращает тип Object , который должен быт приведён к ожидаемому типу с помощью операции приведения типов. Строки и массивы в Java являются объектами. Примитивные типы считываются с помощью методов DataInput ( ObjectInput наследуется от DataInput ) и записываются с помощью методов DataOutput ( ObjectOutput наследуется от DataOutput ).

Запись объекта называется сериализацией.

Чтение объекта называется десериализацией.

Поля, объявленные как transient или static , игнорируются в процессе сериализации/десериализации.

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

В процессе чтения объекты создаются аналогично выполнению конструктора для нового объекта. Сначала выделяется память и заполняется нулями. Для несериализуемых классов вызываются конструкторы по умолчанию, а поля сериализуемых классов считываются из потока, начиная от наиболее близкого к java . lang . Object класса и заканчивая наиболее специфичным классом.

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

Метод readObject отвечает за чтение и восстановление состояния объекта класса, записанного в поток соответствующим методом writeObject . Метод не должен беспокоиться о состоянии, принадлежащем его суперклассам или подклассам. Состояние восстанавливается чтением данных из ObjectInputStream для каждого поля и присвоением этих значений соответствующим полям объекта. Чтение примитивных типов происходит с помощью DataInput .

Любая попытка считать данные, которые выходят за границу данных, записанных методом writeObject , приводит к исключению OptionalDataException . Методы чтения примитивных типов или в массив байт возвращают -1 или бросают исключение EOFException в зависимости от метода.

Сериализация не читает и не присваивает значения полям, которые не реализуют интерфейс java . io . Serializable . Подклассы класса Object , которые не сериализуемые, могут быть сериализуемыми, для этого они должны иметь конструктор по умолчанию. В этом случае задача сохранения и восстановления их состояния ложится на дочерний класс.

Любое исключение в процессе десериализации перехватывается ObjectInputStream -ом и останавливает процесс чтения.

Реализация интерфейса Externalizable позволяет полностью контролировать содержимое и формат сериализации. Методы void writeExternal ( ObjectOutput out ) throws IOException и void readExternal ( ObjectInput in ) throws IOException , ClassNotFoundException интерфейса Externalizable вызываются при сохранении и восстановлении состояния объектов. При реализации каким-нибудь классом они могут записывать и считывать своё состояние с помощью методов ObjectOutput и ObjectInput . Ответственность за обработку версионности ложится на сам объект.

Десериализация констант перечислений отличается от обычной сериализации и десериализации объектов. Сериализованная форма констант содержит только и имена, Значения полей констант опускаются. При десериализации константы ObjectInputStream считывает имя константы из потока, затем вызывает метод Enum . valueOf ( Class , String ) для получения значения. Процесс десериализации перечислений не может быть изменён. Любой метод readObject , readObjectNoData и readResolve , объявленный в перечислении игнорируется. Так же игнорируются поля serialPersistentFields и serialVersionUID , перечисления имеют фиксированный serialVersionUID 0L.

Статическое поле serialVersionUID означает версию класса. Оно объявляется так:

Читайте также: