что такое externalizable и для чего он нужен

Что такое externalizable и для чего он нужен

Последнее изменение: 19 апреля 2011г.

Сериализация как она есть

На первый взгляд, сериализация кажется тривиальным процессом. Действительно, что может быть проще? Объявил класс реализующим интерфейс java.io.Serializable – и все дела. Можно сериализовать класс без проблем.

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

Статью эту можно разделить на следующие части:

Приступим к первой части –

Тонкости механизмов

Прежде всего, вопрос на засыпку. А сколько существует способов сделать объект сериализуемым? Практика показывает, что более 90% разработчиков отвечают на этот вопрос приблизительно одинаково (с точностью до формулировки) – такой способ один. Между тем, их два. Про второй вспоминают далеко не все, не говоря уж о том, чтобы сказать что-то внятное о его особенностях.

Замечание. В дальнейшем сериализацию с реализацией Serializable я буду иногда называть стандартной, а реализацию Externalizable – расширенной.

Между упомянутыми двумя способами сериализации существует еще одно серьезное отличие. А именно – в механизме десериализации. При использовании Serializable десериализация происходит так: под объект выделяется память, после чего его поля заполняются значениями из потока. Конструктор объекта при этом не вызывается.

Тут надо еще отдельно рассмотреть такую ситуацию. Хорошо, наш класс сериализуемый. А его родитель? Совершенно необязательно! Более того, если наследовать класс от Object – родитель уж точно НЕсериализуемый. И пусть о полях Object мы ничего не знаем, но в наших собственных родительских классах они вполне могут быть. Что будет с ними? В поток сериализации они не попадут. Какие значения они примут при десериализации?

Посмотрим на этот пример:

Он прозрачен – у нас есть несериализуемый родительский класс и сериализуемый дочерний. И вот что получается:

То есть при десериализации вызывается конструктор без параметров родительского НЕсериализуемого класса. И если такого конструктора не будет – при десериализации возникнет ошибка. Конструктор же дочернего объекта, того, который мы десериализуем, не вызывается, как и было сказано выше.

Рассмотрим теперь вот какой аспект. Пусть у нас есть такая структура классов:

Иначе говоря, у нас есть класс, унаследованный от несериализуемого родителя. Можно ли сериализовать этот класс, и что для этого надо? Что будет с переменными родительского класса?

Следующий момент, которого я хотел бы коснуться – сериализация нескольких объектов. Пусть у нас есть следующая структура классов:

что такое externalizable и для чего он нужен. Смотреть фото что такое externalizable и для чего он нужен. Смотреть картинку что такое externalizable и для чего он нужен. Картинка про что такое externalizable и для чего он нужен. Фото что такое externalizable и для чего он нужен

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

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

Еще один небольшой тест в подтверждение этого:

Как видим, все четыре десериализованных объекта на самом деле представляют собой один объект – ссылки на него равны. Ровно как это и было до сериализации.

С деталями, пожалуй, закончили. Однако есть один вопрос, который мы не затронули, глобального характера. А именно –

Зачем нужен Externalizable

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

С гибкостью вроде как понятно всё. Действительно, мы можем управлять процессами сериализации и десериализации как хотим, что делает нас независимыми от любых изменений в классе (как я говорил чуть выше, изменения в классе способны сильно повлиять на десериализацию). Потому хочу сказать пару слов о выигрыше по объему.

Допустим, у нас есть следующий класс:

Можно ли как-нибудь ужать эти данные? Наверняка. Секунды и минуты лежат в интервале 0-59, т.е. для их представления достаточно 6 бит вместо 8. Часы – 0-23 (5 бит), дни – 0-30 (5 бит), месяцы – 0-11 (4 бита). Итого, всё без учета года – 26 бит. До размера int еще остается 6 бит. Теоретически, в некоторых случаях этого может хватить для года. Если нет – добавление еще одного байта увеличивает размер поля данных до 14 бит, что дает промежуток 0-16383. Этого более чем достаточно в реальных приложениях. Итого – мы ужали размер данных, необходимых для хранения нужной информации, до 5 байт. Если не до 4.

Недостаток тот же, что и в предыдущем случае – если хранить дату упакованной, то нужны методы преобразования. А хочется так – хранить в отдельных полях, а сериализовать в упакованном виде. Вот тут как раз целесообразно использовать Externalizable :

Собственно, это все. После сериализации мы получаем служебные издержки на класс, два поля (вместо 6) и 5 байт данных. Что уже существенно лучше. Дальшейшую упаковку можно оставить специализированным библиотекам.

Приведенный пример весьма прост. Его основное предназначение – показать, как можно применять расширенную сериализацию. Хотя возможный выигрыш в объеме сериализованных данных – далеко не основное преимущество, на мой взгляд. Основное же преимущество, помимо гибкости. (плавно переходим к следующему разделу. )

Производительность

Как я уже говорил, стандартная сериализация работает через Reflection API. Что означает, что для сериализации берется класс сериализуемого объекта, у него берется список полей, по всем полям в цикле проверяются различные условия ( transient или нет, если объект, то Externalizable или Serializable), значения пишутся в поток, причем достаются из полей тоже через reflection. В общем, ситуация ясна. В противоположность этому методу, вся процедура при использовании расширенной сериализации контролируется самим разработчиком. Осталось выяснить, какие преимущества это дает по скорости.

Полный код теста вместе с build-файлом для ant можно найти тут – serialization.zip. В тексте я буду приводить только отрывки.

Сериализуемый объект содержит следующий набор полей:

Итак, каковы результаты выполнения теста? На 100000 создаваемых объектов (результаты могут незначительно отличаться от запуска к запуску):

И размеры сериализованных данных (размеры файлов на диске):

Что мы видим? Первый способ реализации Externalizable даже несколько хуже стандартной сериализации. Сериализация занимает немного больше времени, десериализация сравнима. Размеры файлов тоже немного в пользу стандартной сериализации. Вывод – простейшая сериализация контейнера преимуществ не дает: +15% при сериализации, десериализация отличается на доли процента, причем как в одну, так и в другую сторону.

Думаю, комментарии излишни. Получаемые от грамотной реализации Externalizable преимущества в скорости с лихвой компенсируют затраты на эту самую реализацию. Грамотной – в смысле, целиком и полностью реализованной самостоятельно, без использования имеющихся механизмов сериализации целых объектов (в основном это методы writeObject/readObject ). Использование же имеющихся механизмов и/или смешивание со стандартной сериализацией способно свести скоростные преимущества Externalizable на нет.

Обратная сторона медали

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

Перейдем к следующему вопросу, связанному с сериализацией.

Безопасность данных

Есть такое правило: проверять входящие данные (входные параметры функций и т.п.) на «правильность» – соответствие определенным требованиям. Причем это не столько правило хорошего тона, сколько правило выживания приложения. Ибо если этого не сделать, то при передаче неверных параметров в лучшем случае (действительно – в лучшем!) приложение просто «упадет». В худшем случае оно тихо примет предложенные данные и может нанести значительно больший урон.

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

Не буду вдаваться в подробности. Описание этого приема есть в книге Джошуа Блох. Java. Эффективное программирование, в статье 56. Скажу только, что достаточно к потоку дописать 5 байт, чтобы добиться желаемого.

Чтобы этого избежать, необходимо следовать следующему правилу:

Правило 2. Если в составе класса A присутствуют объекты, которые не должны быть доступными для изменения извне, то при десериализации экземпляра класса A необходимо вместо этих объектов создать и сохранить их копии.

Приведенные выше примеры показывают возможные «дыры» в безопасности. Следование упомянутым правилам, разумеется, не спасает от проблем, но может существенно снизить их количество. Советую по этому поводу почитать книгу Джошуа Блох. Java. Эффективное программирование, статью 56.

Ну и последняя тема, которой я хотел бы коснуться –

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

Решение же заключается в следующем. В классе определяется метод со следующей сигнатурой

Самое интересное, что, похоже, из этих методов можно возвращать не только экземпляр класса, в котором этот метод определен, но и экземпляр другого класса. Я видел подобные примеры в глубинах библиотек Sun, во всяком случае, для writeReplace – точно видел. Но по каким принципам можно это делать – не берусь пока судить. Вообще, советую интересующимся просмотреть исходники J2SE 5.0, причем полные. Они доступны по лицензии JRL. Там есть много интересных примеров использования этих методов. Исходники можно взять тут – http://java.sun.com/j2se/jrl_download.html. Правда, требуется регистрация, но она, естественно, бесплатна.

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

Источник

Externalizable в Java

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

Externalizable:

Как следует из названия, он выполняет внешнюю сериализацию. Если вы хотите настроить механизм сериализации, вы можете использовать его. Он использует собственный письменный механизм для выполнения маршалинга и демаршаллинга объектов. Интерфейс Externalizable расширяет интерфейс Serializable. Если вы реализуете этот интерфейс, вам нужно переопределить следующие методы.

Теперь давайте посмотрим, как происходит сериализация:

На стороне отправителя:

JVM проверяет, реализует ли класс externalizable или нет. Если он затем сериализует объект с помощью метода writeExternal (). Если он не реализует externalizable, но реализует сериализуемый, объект сериализуется с помощью ObjectOutputStream.

На стороне получателя:

Когда объект восстанавливается и он доступен для внешнего использования, экземпляр создается без конструктора args и вызывается readExternal. Если это не внешний объект, а сериализуемый, объект восстанавливается с помощью ObjectInputStream.

Создайте Employee.java в src-> org.arpit.javapostsforlearning

у вас не должно быть конструктора args, если вы реализуете externalizable.

Создайте ExternalizableMain.java в org.arpit.javapostsforlearning.

Запустите его: при запуске ExternalizableMain.java. Вы получите следующий вывод

Вы действительно хотите столько накладных расходов, когда все, что вы хотите сериализовать, это employeeId и employeeName

Наследование в экстернализации:

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

Мы создадим Person.java, который будет суперклассом Employee.

что такое externalizable и для чего он нужен. Смотреть фото что такое externalizable и для чего он нужен. Смотреть картинку что такое externalizable и для чего он нужен. Картинка про что такое externalizable и для чего он нужен. Фото что такое externalizable и для чего он нужен

Случай 1. Что если суперкласс не реализует Externalizable:

Если суперкласс не реализует externalizable, вам нужно сериализовать поля суперкласса в подклассе, который реализует Externalizable.

Источник

Русские Блоги

Понимание Externalizable и Serializable

Взаимоотношения

Прежде всего, оба являются интерфейсами. Externalizable наследует Serializable и добавляет два объявления метода, а именно writeExternal и readExternal. Я нарисовал UML-диаграммы, чтобы показать их взаимосвязь следующим образом:

что такое externalizable и для чего он нужен. Смотреть фото что такое externalizable и для чего он нужен. Смотреть картинку что такое externalizable и для чего он нужен. Картинка про что такое externalizable и для чего он нужен. Фото что такое externalizable и для чего он нужен

2.Serializable Введение

(1) Интерфейс Serializable не имеет определенных методов и постоянных определений, поэтому это пустой интерфейс. Если класс реализует интерфейс Serializable, это эквивалентно сообщению JVM, что этот класс сериализуем (Интерпретация сериализации) Из.

(2) Для класса, который реализует интерфейс Serializable, если вам требуется исключить переменную-член класса во время сериализации и не участвовать в сериализации, вы можете использовать ключевое слово transient при определении переменной-члена, например: private transient int a;

(3) serialVersionUID, серийный номер версии. При реализации интерфейса Serializable вы можете явно определить константу serialVersionUID или нет. Если он не определен, он будет автоматически сгенерирован определенным алгоритмом в соответствии с именем класса, именем метода, именем переменной-члена и т. Д. По умолчанию во время компиляции. Он используется для определения версии класса. Например, после сериализации вы изменили исходный класс.Если вы явно указали serialVersionUID, десериализацию все еще можно выполнить в обычном режиме, поскольку UID не изменился. В противном случае, если вы измените имя класса, метода или переменной-члена, автоматически сгенерированный UID будет отличаться от сериализации, и десериализация завершится неудачно. Обычно вы видите приглашение, подобное этому:

что такое externalizable и для чего он нужен. Смотреть фото что такое externalizable и для чего он нужен. Смотреть картинку что такое externalizable и для чего он нужен. Картинка про что такое externalizable и для чего он нужен. Фото что такое externalizable и для чего он нуженТестовый код выглядит следующим образом:

3. Введение в Externalizable

(1) Методы writeExternal и readExternal, предоставляемые интерфейсом Externalizable, предоставляют разработчикам контроль над реализацией сериализации (вы можете решить, какие поля участвуют в сериализации, или изменить некоторые значения полей до и после сериализации и т. Д.)

(2) При реализации интерфейса Externalizable, Входными параметрами методов writeExternal и readExternal являются ObjectOutputStream и ObjectInputStream, которые могут вызывать только свои методы writeObject и readObject, а другие методы не могут завершить сериализацию.

Источник

Русские Блоги

Сериализация и десериализация объектов (Serializable, Externalizable)

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

Концепция сериализации объектов была добавлена ​​в язык для обеспечения поддержки двух основных функций:

1 Удаленный вызов метода

2 、 Java Beans Государственное сохранение и восстановление

ObjectInput Наследование интерфейса DataInput интерфейс

ObjectOutput Наследование интерфейса DataOutput интерфейс

ObjectOutputStream Класс реализует DataOutput,ObjectOutput

ObjectInputStream Класс реализует DataInput,ObjectInput

ObjectOutputStream.defaultWriteObject() : Записывает в этот поток нестатические и непереходные поля текущего класса.

ObjectInputStream.defaultReadObject() : Читать из этого потока нестатические и непереходные поля текущего класса.

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

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

использование Сериализация Сериализуемый

В следующем примере показано, как реализовать сериализацию с помощью интерфейса тега Serializable:

Результаты прогона следующие:

—- Начните строить Wrom
Worm constructor: 6
Worm constructor: 5
Worm constructor: 4
Worm constructor: 3
Worm constructor: 2
Worm constructor: 1
w = :A(6 8 7):B(9 2 1):C(4 7 8):D(4 1 7):E(4 0 8):F(9 7 3)
—- начать сериализацию
—- Начать десериализацию
Worm storage
w3 = :A(6 8 7):B(9 2 1):C(4 7 8):D(4 1 7):E(4 0 8):F(9 7 3)

Используйте Externalizable сериализацию

readExternal(ObjectInput in) throws IOException,CalssNotFoundException

writeExternal(ObjectOutput out) throws IOException,CalssNotFoundException

Эти два метода вызываются автоматически во время сериализации и реорганизации для выполнения некоторых специальных операций.

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

И для одного Externalizable Object, конструктор по умолчанию вызывается первым при десериализации А потом позвони readExternal()

Примечание: Метод сериализации (writeObject) не выполняется автоматически transient Сериализация атрибутов и статических атрибутов ( от API в ObjectOutputStream. defaultWriteObject() Описание способа показывает такой вывод, который описывается следующим образом: Write the non-static and non-transient fields of the current class to this stream.)

Для достижения Внешние шаги интерфейса Следующим образом:

1 Для достижения Externalizable интерфейс

2 Для достижения writeExternal() В методе укажите, какие объекты сериализуются, если он не сохранен, вы не сможете сохранить состояние свойства.

3 、 Для достижения readExternal() В этом методе укажите, какие объекты десериализованы. Если вы не читаете, вы не сможете восстановить состояние свойства.

Externalizable Процесс восстановления состояния объекта выглядит следующим образом:

1 、 Вызовите конструктор объекта по умолчанию ( Примечание: Конструктор по умолчанию должен быть public из Ничего другого, без ошибок при десериализации )

2 по readExternal() Дальнейшее восстановление отдельных атрибутов

В следующем примере показано, как полностью сохранить и восстановить объект Externalizable:

—Constructing objects:
Blip3(String x, int a)
A String 47
—Saving object:
Blip3.writeExternal
—Recovering b3:
Blip3 Constructor
Blip3.readExternal
A String 47

управляемый Serializable Сериализация ( Внешняя альтернатива)

Поскольку Externalizable объекты не сохраняют ни один из своих доменов по умолчанию,Ключевое слово transient может использоваться только с сериализуемыми объектами.

private void writeObject(ObjectOutputStream stream) throws IOException;

private void readObject(ObjectInputStream stream) throws IOException,CalssNot FoundException

С точки зрения дизайна, сейчас все действительно невероятно. Они определены как private Это означает, что они не могут быть вызваны другими членами этого класса. Тем не менее, мы на самом деле не вызываем их из других методов этого класса, но ObjectOutputStream и ObjectInputStream объекты writeObject() и readObject() Вызов метода нашего объекта writeObject() и readObject() метод 。 Прежде чем позвонить ObjectOutputStream.writeObject() Проверим что ты сдашь Serializable Объект, посмотрите, реализован ли он ( Чтобы быть точным, это должно быть добавлено ) Получил свой writeObject() Если так, пропустите нормальный процесс сериализации и назовите это writeObject() 。 readObject() Ситуация такая же.

Примечание: Если некоторые достигают Serializable Интерфейс и добавил writeObject() и readObject() Класс метода для сохранения не transient Часть, тогда мы должны позвонить defaultWriteObject() Работать как writeObject() Первая операция и пусть defaultReadObject() в качестве readObject() Первая операция, если нет, мы можем только вручную сохранить и восстановить по одному 。

В следующем примере показано, как управлять хранением и восстановлением объекта Serializable:

Before:
Not Transient: Test1
Transient: Test2
After:
Not Transient: Test1
Transient: Test2

Еще один контролируемый Serializable Сериализация

Для получения подробной информации, пожалуйста, обратитесь к«Как много вы знаете о Java (7)»[54. Одиночная проблема с сериализуемым]

Один и тот же объект сериализуется несколько раз в один и тот же выходной поток и разные выходные потоки.

Что произойдет, если мы сериализуем два объекта, которые имеют ссылку на третий объект?

Когда мы восстановим эти два объекта из их сериализованного состояния, третий объект появится только один раз?

Что если вы сериализуете эти два объекта в отдельные файлы и десериализуете их в разных частях кода?

Смотрите этот пример разбивки:

Результат: адреса этих десериализованных объектов должны отличаться от адресов исходных объектов. Обратите внимание, однако, что один и тот же адрес появляется у животных1 и животных2, включая ссылку на дом, который является общим для обоих. С другой стороны, при восстановлении животных3 система не может знать, что объекты в другом потоке являются псевдонимами объектов в первом потоке, поэтому она создаст совершенно другую сеть объектов.

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

ObjectOutputStream.writeUnshared

Записывает «неразделенные» объекты в ObjectOutputStream. Этот метод эквивалентен writeObject, за исключением того, что он всегда записывает данный объект как единственный новый объект в потоке (в отличие от обратной ссылки на ранее сериализованный экземпляр). В частности:

Хотя запись объекта через writeUnshared сама по себе не гарантирует уникальность ссылки на объект при десериализации объекта, она позволяет определять один объект несколько раз в потоке, поэтому множественные вызовы readUnshared получателем не вызовут конфликтов. Обратите внимание, что приведенные выше правила применяются только к объекту базового уровня (саму сериализованному объекту), написанному writeUnshared, и не могут применяться к подобъектам, на которые ссылаются любым изменяемым способом в объектной графике, подлежащей сериализации (то есть сериализованная версия не будет создана снова Член объекта).

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

Сериализованные результаты работы:

Инициализировать статическую переменную i
Переменная инициализации j
Call build

Результаты десериализации:

Инициализировать статическую переменную i
Нестатическая переменная j = 22
// Примечание: вы видите, что метод prt (22) не был вызван при восстановлении нестатической переменной j, но состояние было восстановлено
Статическая переменная i = 1 // Описывает, что статическая переменная не была восстановлена. Если вы хотите восстановить состояние статической переменной, вы можете только вручную сериализовать ее.
переходная переменная y = 0 // Описывает, что временная переменная не была восстановлена. Если вы хотите восстановить состояние статической переменной, вы можете только вручную сериализовать ее.

Поэтому, если вы хотите сериализовать статические и временные элементы, мы можем только передать Externalizable Или управляемый Serializable Для достижения.

Где этот атрибут не должен быть сериализован?

Не все поля и свойства класса, который реализует интерфейс сериализации, являются сериализуемыми:

Управление несколькими версиями классов сериализации

Если в десериализованной JVM существуют разные версии этого класса, что нам делать?

Проблемы возникают : Если сделано несколько модификаций, будет получена различная информация об отпечатке пальца. Когда сериализованный объект, информация об отпечатке которого изменилась, десериализован в другой виртуальной машине, информация об отпечатке пальца класса на другой виртуальной машине и отпечаток десериализованного объекта будут отличаться. Информация отличается, поэтому исключение происходит во время обратной последовательности. Давайте сделаем эксперимент:
1. Например, есть такой класс:

2. Теперь мы сериализуем его в файл, код выглядит следующим образом:

3. Теперь давайте изменим SerialClass и добавим поле:

4. Теперь давайте десериализовать, используйте следующий код:

Из вышеприведенного исключения видно, что поскольку класс обновляется, информация об отпечатке пальца изменяется и возникает ошибка во время десериализации.

Решения : При необходимости сериализации класса SerialClass с частным статическим атрибутом окончательной версии long serialVersionUID и значением является значением отпечатка перед модификацией-7303985972226829323L, так что код не будет автоматически требоваться для создания информации отпечатка пальца во время компиляции, но для этого нам нужно Указанный отпечаток. Модифицированный код выглядит следующим образом:

Теперь давайте возьмем еще один, и результат нормальный.

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

Источник

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

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