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

В чем разница между Serializable и Externalizable в Java?

В чем разница между Serializable и Externalizable в Java?

ОТВЕТЫ

Ответ 1

В последних версиях Java (начиная с версии 1.3, конечно) производительность отражения намного лучше, чем раньше, и поэтому это гораздо менее проблематично. Я подозреваю, что вам будет трудно получить значимую выгоду от Externalizable с современной JVM.

Кроме того, встроенный механизм сериализации Java не является единственным, вы можете получить сторонние замены, такие как JBoss Serialization, что значительно быстрее и является заменой по умолчанию.

Таким образом, Externalizable является реликвией Java 1.1 дней. Нет необходимости в этом больше.

Ответ 2

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

Но если вы хотите ограничить сериализацию или не хотите, чтобы часть вашего объекта была сериализована, используйте Externalizable. Интерфейс Externalizable расширяет интерфейс Serializable и добавляет два метода: writeExternal() и readExternal(). Они автоматически вызывается при сериализации или десериализации. Во время работы с Externalizable мы должны помнить, что конструктор по умолчанию должен быть открытым, иначе код будет генерировать исключение. Пожалуйста, следуйте приведенному ниже коду:

Здесь, если вы прокомментируете конструктор по умолчанию, тогда код выйдет за исключение:

Мы можем заметить, что поскольку пароль является конфиденциальной информацией, поэтому я не сериализую его в методе writeExternal (ObjectOutput oo) и не устанавливаю его значение в readExternal (ObjectInput oi). Это гибкость, предоставляемая Externalizable.

Результат приведенного выше кода выглядит следующим образом:

Мы можем наблюдать, поскольку мы не устанавливаем значение passWord так, чтобы оно было нулевым.

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

Надеюсь, это поможет. Прошу прощения, если я допустил ошибки. Спасибо.

Ответ 3

Только для полноты ключевое слово transient также закрывает промежуток между ними.

Ответ 4

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

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

Ответ 5

Основные различия между Serializable и Externalizable

Ответ 6

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

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

Ответ 7

Интерфейс Externalizable фактически не предоставлялся для оптимизации производительности процесса сериализации! но и предоставить средства для реализации собственной пользовательской обработки и предложить полный контроль над форматом и содержимым потока для объекта и его супер-типов!

Примерами этого является реализация удаленного формата AMF (ActionScript Message Format) для переноса собственных действий script по сети.

Ответ 8

Сериализация объектов использует интерфейсы Serializable и Externalizable. Объект Java является сериализуемым. если класс или любой из его суперклассов реализует либо интерфейс java.io.Serializable, либо его подчиненный интерфейс, java.io.Externalizable. Большинство классов java являются сериализуемыми.

Сериализация объектов создает поток с информацией о классах Java для сохраняемых объектов. Для сериализуемых объектов сохраняется достаточная информация для восстановления этих объектов, даже если присутствует другая (но совместимая) версия реализации класса. Интерфейс Serializable определен для идентификации классов, которые реализуют сериализуемый протокол:

Для объектов Externalizable содержимое контейнера сохраняется только с идентификатором класса объекта; класс должен сохранять и восстанавливать содержимое. Интерфейс Externalizable определяется следующим образом:

OptionalDataException » Поля должны быть в том же порядке и типе, как мы писали их. Если в потоке есть какое-либо несоответствие типа, это исключает Необязательное исключение.

Пример « реализует Serializable

Пример «реализует Externalizable

Ответ 9

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

Ответ 10

Существует так много различий между Serializable и Externalizable, но когда мы сравниваем разницу между пользовательскими Serializable (переопределенными writeObject() и readObject()) и Externalizable, тогда мы обнаруживаем, что пользовательская реализация тесно связана с классом ObjectOutputStream, где, как и в случае Externalizable, мы сами обеспечиваем реализацию ObjectOutput, который может быть классом ObjectOutputStream или может быть другим, например org.apache.mina.filter.codec.serialization.ObjectSerializationOutputStream

В случае интерфейса Externalizable

Я добавил пример кода, чтобы лучше объяснить. пожалуйста, проверьте в/из объекта случай Externalizable. Они не привязаны к какой-либо реализации напрямую.
Где, поскольку Outstream/Instream тесно связаны с классами. Мы можем расширить ObjectOutputStream/ObjectInputStream, но это будет немного сложно использовать.

Источник

Знакомство с интерфейсом Externalizable

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

Во-первых, внутренний механизм Serializable во время работы генерирует большой объем служебной информации и разного рода временных данных.
Во-вторых (в это можешь сейчас не углубляться и почитать на досуге, если интересно), работа Serializable основана на использовании Reflection API. Эта штуковина позволяет делать, казалось бы, невозможные в Java вещи: например, менять значения приватных полей. На JavaRush есть отличная статья про Reflection API, можешь почитать о ней здесь.

Читайте также:  Что в моей сумке челлендж

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

По сути, все что у нас есть для управления процессом, — это ключевое слово transient для исключения каких-либо данных, и все. Такой себе «инструментарий» :/

Безопасность.Этот пункт частично вытекает из предыдущего.

Мы раньше особо над этим не задумывались, но что делать, если какая-то информация в твоем классе не предназначена для «чужих ушей» (точнее, глаз)? Простой пример — пароль или другие персональные данные пользователя, которые в современном мире регулируются кучей законов.

А ведь, по-хорошему, такого рода данные мы должны зашифровать перед записью в файл или передачей по сети. Но Serializable этой возможности не дает.

Источник

Сериализация в 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(). Они отвечают за сериализацию по умолчанию, как если бы она работала без добавленных нами методов.

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

Источник

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

Второй вывод, более суще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. Правда, требуется регистрация, но она, естественно, бесплатна.

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

Источник

Универсальный бизнес портал