Как работает сериализация в Java и когда ее следует использовать вместо какого-либо другого метода сохранения?
В последнее время я пытался узнать больше и в целом тестировать сериализацию 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?
9 ответов
Я бы лично попытался избежать "встроенной" сериализации Java:
- Это не переносимо на другие платформы
- Это не очень эффективно
- Это хрупко - заставить его справиться с несколькими версиями класса довольно сложно. Даже изменение компиляторов может нарушить сериализацию, если вы не будете осторожны.
Для деталей того, что означают фактические байты, посмотрите Спецификацию Сериализации Объекта Java.
Существуют различные альтернативы, такие как:
- XML и JSON, как вы показали (разные XML-варианты, конечно)
- YAML
- Facebook Thrift (RPC, а также сериализация)
- Google Protocol Buffers
- Гессиан (веб-сервисы, а также сериализация)
- Апач Авро
- Ваш собственный формат
(Отказ от ответственности: я работаю на Google, и я делаю перенос протокольных буферов на C# как мой 20% проект, так что я думаю, что это хорошая технология:)
Кроссплатформенные форматы почти всегда более ограничительны, чем форматы для конкретных платформ, по очевидным причинам - например, у Protocol Buffers довольно ограниченный набор нативных типов - но совместимость может быть невероятно полезной. Вам также необходимо учитывать влияние версий, с обратной и прямой совместимостью и т. Д. Текстовые форматы обычно редактируются вручную, но, как правило, менее эффективны как в пространстве, так и во времени.
По сути, вам нужно внимательно изучить ваши требования.
Основным преимуществом сериализации является то, что она чрезвычайно проста в использовании, относительно быстра и сохраняет реальные сетки объектов Java.
Но вы должны понимать, что на самом деле он предназначен не для хранения данных, а главным образом для обмена данными между различными экземплярами JVM по сети с использованием протокола RMI.
См . Java Stream Object Serialization Stream Protocol для описания формата файла грамматики, используемой для сериализованных объектов.
Лично я считаю, что встроенная сериализация приемлема для сохранения недолговечных данных (например, для сохранения состояния объекта сеанса между http-запросами), который не имеет отношения вне вашего приложения.
Для данных, которые имеют более длительное время жизни или должны использоваться вне вашего приложения, я бы сохранил их либо в базе данных, либо, по крайней мере, в более широко используемом формате...
Как работает встроенная сериализация в Java?
Всякий раз, когда мы хотим сериализовать объект, мы реализуем интерфейс java.io.Serializable. Интерфейс, который не имеет каких-либо методов для реализации, хотя мы реализуем его, чтобы указывать что-то компилятору или JVM (известный как интерфейс маркера). Поэтому, если JVM видит, что класс Serializable, он выполняет некоторую операцию предварительной обработки этих классов. Операция заключается в добавлении следующих двух примеров методов.
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
}
Когда это должно использоваться вместо некоторой другой техники постоянства?
Встроенный Serialization
полезно, когда отправитель и получатель оба являются Java. Если вы хотите избежать вышеуказанных проблем, мы используем XML или JSON с помощью фреймворков.
Причина, по которой хранение небольшого количества информации является последовательной формой, относительно велика, потому что она хранит информацию о классах объектов, которые она сериализует. Если вы сохраните дубликат вашего списка, то увидите, что файл сильно не вырос. Храните один и тот же объект дважды, и разница крошечная.
Важные плюсы: относительно прост в использовании, довольно быстр и может развиваться (как XML). Тем не менее, данные довольно непрозрачны, они только для Java, тесно связывают данные с классами, и ненадежные данные могут легко вызвать DoS. Вы должны думать о сериализованной форме, а не просто шлепать implements Serializable
везде.
Преимущество Java Object Serialization (JOS) в том, что он просто работает. Есть также инструменты, которые делают то же самое, что и JOS, но используют формат XML вместо двоичного формата.
О длине: JOS записывает некоторую информацию о классе в начале, а не как часть каждого экземпляра - например, полные имена полей записываются один раз, а индекс в этом списке имен используется для экземпляров класса. Это удлиняет вывод, если вы пишете только один экземпляр класса, но более эффективно, если вы пишете несколько (разных) его экземпляров. Мне не ясно, использует ли ваш пример класс, но это основная причина, почему JOS длиннее, чем можно было ожидать.
Кстати: это случайно, но я не думаю, что JSON записывает имена классов (как у вас в вашем примере), и поэтому он может не выполнять то, что вам нужно.
Сериализация означает, что вы помещаете свои структурированные данные в классы в единый порядок байт-кода, чтобы сохранить их.
Как правило, вы должны использовать другие методы, кроме встроенного java-метода, он просто предназначен для работы "из коробки", но если в ваших сериализованных классах будут какие-то изменения содержимого или изменения порядка в будущем, у вас возникнут проблемы, потому что вы не сможете загрузить их правильно.
Если у вас не слишком много данных, вы можете сохранить объекты в объект java.util.Properties. Примером пары ключ / значение может быть user_1234_firstname = Peter. Использование отражения для сохранения и загрузки объектов может упростить задачу.
Я столкнулся с этой дилеммой около месяца назад (см. Вопрос, который я задал).
Главный урок, который я извлек из этого, - это использование сериализации Java только тогда, когда это необходимо, и если нет другого выбора. Как сказал Джон, у него есть свои недостатки, в то время как другие методы сериализации намного проще, быстрее и более переносимы.