В последнее время я пытался узнать больше и в целом протестировать сериализацию Java как для работы, так и для личных проектов, и должен сказать, что чем больше я знаю об этом, тем меньше мне это нравится. Это может быть вызвано дезинформацией, поэтому я прошу вас всех двух вещей:
1: На уровне байтов, как сериализация знает, как сопоставлять сериализованные значения с некоторым классом?
Одна из моих проблем заключается в том, что я провел небольшой тест с ArrayList, содержащим значения «один», «два», «три». После сериализации байтовый массив занял 78 байтов, что кажется ужасно большим для такого небольшого количества информации (19 + 3 + 3 + 4 байта). Конечно, будут накладные расходы, но это приводит ко второму вопросу:
2: Можно ли вообще считать сериализацию хорошим методом сохранения объектов? Теперь очевидно, что если бы я использовал какой-то самодельный формат XML, данные о сохранении были бы примерно такими
<object>
<class = "java.util.ArrayList">
<!-- Object array inside Arraylist is called elementData -->
<field name = "elementData">
<value>One</value>
<value>Two</value>
<value>Three</value>
</field>
</object>
который, как и XML в целом, немного раздут и занимает 138 байт (то есть без пробелов). То же самое в JSON может быть
{
"java.util.ArrayList": {
"elementData": [
"one",
"two",
"three"
]
}
}
который составляет 75 байт, что немного меньше, чем сериализация Java. С этими текстовыми форматами, конечно, очевидно, что должен быть способ представить ваши основные данные в виде текста, чисел или любой их комбинации.
Итак, напомним, как сериализация работает на уровне байтов / битов, когда ее следует использовать, а когда не следует, и каковы реальные преимущества сериализации, помимо того, что она входит в стандартную комплектацию Java?




Я бы лично попытался избежать "встроенной" сериализации Java:
Для получения подробной информации о том, что означают настоящие байты, см. Спецификация сериализации объектов Java.
Существуют различные альтернативы, такие как:
(Отказ от ответственности: я работаю в Google, и я делаю перенос протокольных буферов на C# в качестве своего 20-процентного проекта, поэтому я думаю, что это хорошая технология :)
Кросс-платформенные форматы почти всегда более строгие, чем форматы для конкретных платформ по очевидным причинам - например, Protocol Buffers имеет довольно ограниченный набор собственных типов - но возможность взаимодействия может быть невероятно полезной. Вам также необходимо учитывать влияние управления версиями на обратную и прямую совместимость и т. д. Текстовые форматы обычно редактируются вручную, но, как правило, менее эффективны как в пространстве, так и во времени.
По сути, вам нужно внимательно посмотреть на свои требования.
Другой альтернативой было бы хранить его в базе данных, такой как SQLite, особенно если объекты не глубоко вложены.
Я начал путь java-сериализации для некоторых не очень важных файлов, которые нам нужно было сохранить, потому что это было «просто». Только этого не было, потому что у нас были проблемы с наследованием. Я решил все это, но был недоволен тем, что не мог читать файлы. Затем я понял, что, поскольку мы уже использовали JSON для нашего проводного протокола, я мог бы использовать JSON для сериализации. Все это работало без изменения всех классов данных для реализации Serializable и написания методов readObject (). И вообще, как, черт возьми, они вызывают частные методы, такие как readObject () и writeObject (). Так намного счастливее.
@JonSkeet: Не могли бы вы уточнить внутреннюю архитектуру этого интерфейса маркера и Когда нам следует перейти на сериализацию
@VedPrakash: Я не уверен, что вы имеете в виду под «внутренней архитектурой этого интерфейса маркера», но с точки зрения того, когда вам следует использовать собственную двоичную реализацию сериализации Java, я бы почти всегда избегал этого, используя одну из схем, перечисленных в вместо этого мой ответ.
@JonSkeet: Получил ваш ответ и понял.
Я столкнулся с этой дилеммой около месяца назад (см. вопрос, который я задал).
Главный урок, который я извлек из этого, - использовать сериализацию Java только тогда, когда это необходимо и если нет другого варианта. Как сказал Джон, у него есть свои недостатки, в то время как другие методы сериализации намного проще, быстрее и портативнее.
Сериализация означает, что вы помещаете свои структурированные данные в свои классы в простой порядок байт-кода для их сохранения.
Как правило, вы должны использовать другие методы, кроме встроенного java-метода, он просто создан для работы из коробки, но если у вас есть какое-то изменение содержимого или изменение порядка в будущем в ваших сериализованных классах, у вас возникнут проблемы, потому что вы не сможете загрузить их правильно.
см. объект Java Потоковый протокол сериализации для описания формата файла и грамматики, используемой для сериализованных объектов.
Лично я считаю, что встроенная сериализация приемлема для сохранения краткосрочных данных (например, для сохранения состояния объекта сеанса между http-запросами), что не имеет отношения к вашему приложению.
Для данных, которые имеют более продолжительное время жизни или должны использоваться вне вашего приложения, я бы сохранил либо в базе данных, либо, по крайней мере, использовал более часто используемый формат ...
Я согласен. Это предназначалось для чего-то вроде передачи объекта по сети или активации / пассивирования каких-то вещей, а не для сохранения объектов или для внешнего использования.
Основное преимущество сериализации состоит в том, что она чрезвычайно проста в использовании, относительно быстра и сохраняет реальные сетки объектов Java.
Но вы должны понимать, что на самом деле он не предназначен для использования для хранения данных, а в основном как способ для различных экземпляров JVM общаться по сети с использованием протокола RMI.
Преимущество сериализации объектов Java (JOS) в том, что она просто работает. Существуют также инструменты, которые делают то же самое, что и JOS, но используют формат XML вместо двоичного.
О длине: JOS записывает некоторую информацию о классе в начале, а не как часть каждого экземпляра - например, полные имена полей записываются один раз, и индекс в этом списке имен используется для экземпляров класса. Это увеличивает длину вывода, если вы пишете только один экземпляр класса, но более эффективно, если вы пишете несколько (разных) его экземпляров. Мне не ясно, действительно ли в вашем примере используется класс, но это общая причина, по которой JOS длиннее, чем можно было бы ожидать.
Кстати: это случайно, но я не думаю, что JSON записывает имена классов (как в вашем примере), и поэтому он может не делать то, что вам нужно.
Причина, по которой хранение небольшого количества информации в последовательной форме относительно велика, заключается в том, что в ней хранится информация о классах объектов, которые она сериализует. Если вы сохраните копию своего списка, вы увидите, что размер файла не сильно увеличился. Сохраните один и тот же объект дважды, и разница будет крошечной.
Важные плюсы: относительно проста в использовании, довольно быстро и может развиваться (как XML). Однако данные довольно непрозрачны, они предназначены только для Java, тесно связывают данные с классами, а ненадежные данные могут легко вызвать DoS. Вам следует подумать о сериализованной форме, а не просто повсюду использовать implements Serializable.
Если у вас не слишком много данных, вы можете сохранить объекты в объект java.util.Properties. Пример пары ключ / значение: user_1234_firstname = Peter. Использование отражения для сохранения и загрузки объектов может упростить задачу.
Я бы не стал предлагать писать собственный письменный API.
How does Java's built-in serialization works?
Когда мы хотим сериализовать объект, мы реализуем интерфейс java.io.Serializable. Интерфейс, у которого нет никаких методов для реализации, хотя мы реализуем его для указать что-то компилятору или JVM (известного как Маркер Интерфейс). Поэтому, если JVM видит, что класс является сериализуемым, она выполняет некоторую операцию предварительной обработки для этих классов. Операция заключается в добавлении следующих двух примеров методов.
private void writeObject(java.io.ObjectOutputStream stream)
throws IOException {
stream.writeObject(name); // object property
stream.writeObject(address); // object property
}
private void readObject(java.io.ObjectInputStream stream)
throws IOException, ClassNotFoundException {
name = (String) stream.readObject(); // object property
address = (String) stream.readObject();// object property
}
When it should be used instead of some other persistence technique?
Встроенный Serialization полезен, когда отправитель и получатель являются Java. Если вы хотите избежать подобных проблем, мы используем XML или JSON с помощью фреймворков.
Поскольку это каким-то образом привело к горячим вопросам, я хотел бы добавить Авро в список.