-
Notifications
You must be signed in to change notification settings - Fork 3
Serialization
###Примеры кода:
Все примеры в модуле code-examples
пакета serialization
.
Примеры
Проще всего сделать объект сериализуемым - это реализовать java.io.Serializable
интерфейс.
Многие же и вовсе считают, что так только и можно сделать.
Но нет, есть еще способы.
Мы можем:
- реализовать
java.io.Serializable
- реализовать
Externalizable
Подробнее о каждом - ниже.
Простейший и самый часто используемый вариант. Стандартная сериализация в java работает через Reflection API, класс раскладывается на поля, метаданные и пишется в output. Важно понимать, что раз мы используем Reflection API - это не слишком оптимально по части производительности. И еще важное замечание - при использовании стандартной сериализации мы не вызываем конструктор класса при десереализации. //todo deserialization
Теперь рассмотрим такой случай. У нас есть класс у которого также есть супер-класс родитель. Наш класс - serializable, будет ли родитель serializable? Нет, не будет! Тогда как будет работать при дессериализации? Когда мы попытаемся дессериализовать наш класс - Мы вызовем конструктор супер класса без параметров! Сам же конструктор дессериализуемого класса не вызовется. Если у супер класса нет такого конструктора - мы поймаем ошибку. //todo
Еще один интересный момент: Если мы реализуем Serializable интерфейс и в класс добавим методы:
private void readObject(java.io.ObjectInputStream stream) throws IOException, ClassNotFoundException;
private void writeObject(java.io.ObjectOutputStream stream) throws IOException;
private void readObjectNoData() throws ObjectStreamException;
И когда мы попытаемся сериазовать объект - java вызовет эти методы! И будет писать/читать в поток уже с помощью них, поэтому использовать эти методы надо только в случае, если нам необходимо четко и гибко контроллировать весь процесс сериализации/дессериализации. При этом обязательно вызывайте default-методы, которые обеспечат стандартную сериализацию, а потом уже вносите какие-то свои изменения.
В writeObject, например:
private void writeObject(java.io.ObjectOutputStream stream) throws IOException {
stream.defaultWriteObject(); // default serialization
//our special serialization
}
Аналогично и тут:
В readObject, например:
private void readObject(java.io.ObjectInputStream stream) throws IOException, ClassNotFoundException {
stream.defaultReadObject(); // default deserialization
//our special deserialization and using setters
}
Выводы и советы:
- Если родительский класс - Serializable - все дочерние классы - serializable.
- Если дочерний класс Serializable, но родительский нет - поля родительского класса заполнятся как в дефолтном конструкторе.
-
java.io.Serializable
- это как бы метка, что класс у нас сериализуемый. - Процессом serialization and deserialization можно управлять добавив специальные методы в класс.
- При Serialization порядок записи: метаданные класса, метаданные родителя, данные родителя , данные класса.
- При Deserialization порядок записи: метаданные родителя, метаданные класса, данные класса, данные родителя.
- Если у нас есть поле-ссылка на не Serializable класс и это поле не
null
- мы словим ошибку.
Another approach!
Instead of java.io.Serializable
this interface contains two methods – writeExternal(ObjectOutput)
and readExternal(ObjectInput)
. All logic of serialization and deserialization is here. We don't write any metadata of parent classes!
We only call implplemented method - and this method serialize out class as we want. It's very flexible way. And often we write less information than when we use first way. Cause we don't write metadata, meta information!
How it works?
First of all we call default constructor. And that is why we MUST have public default constructor. All children should have it too.
After that on new object of our class we call methods(readExternal) and all our fields are filling data.
If field is transient
- it should have default value.
To sum up:
- If we implement both interfaces - Externalizable and Serializable - the Externalizable has high priority.
- Externalizable is more flexible.
- Externalizable has a good gain in terms of the serialized data.
And it's important to say:
-
Static
fields doesn't serialize in standart approach, but in theory we can(don't do it!) serialize it by Externalizable. - We can't deserialize
final
fields of class - cause we use consrtuctor. - After deserialization we MUST check our object for correctness - and throw
java.io.InvalidObjectException.
if our object is wrong. *And we have difficulties when we want to serialzie/deserialize Singleton object.
Why we have private static final long serialVersionUID? This field contains unique id version of serizalization class. It calculates by fields, order of field declaring, methods and etc. And if we change class state - we change this id. And this field writes to the serialization stream, when we deserialize our object we compare ids and if anything is wrong - throw exception. Java strongly recommended to declare this field. It's your guarantee that all process was correct.
##XML Serialization
We can use another serialization in java - just serialize our object in xml. It's not binary serialization. How we can do it?
Java Architecture for XML Binding.
With this approach we can use XML-schema(XSD)and match our Java object with XML-documents. It's also known as marshalling and demarshalling processes. JAXB can generate XSD, validate schemes. And it's configure with Annotations.
JAXB in Java SE and it's another plus.
- First of all we should annotate like
@XmlRootElement
the main, root, element. We can have only one root element. - Secondly we should annotate accessor type -
@XmlAccessorType(XmlAccessType.____)
- replace ___ as you want, for example put -XmlAccessType.NONE
.
A little bit more information about it:
- XmlAccessType.NONE - We save only annotated elements(fields).
- XmlAccessType.FILED - We save all fields but whithout static and transient fields.
- XmlAccessType.PROPERTY - All getters/setters pairs and annotated fields.
- XmlAccessType.PUBLIC - All public fields and annotated fields.
I think thats all clear. Let's continue.
- After that annotate all fields which you want by
@XmlElement
. It's not neccessary that your field have getters and setters. Cause we use Reflection.
If we have collections - we can use XmlElementWrapper annotation. It's all simple and you can see examples in code. JAXB has a lot of annotations, which can help you to save your object as you want, for example, you can set the order of writing fields in xml and etc.
##JSON Serialization
JSON is very good choice cause it has a good syntax, a lot of libs, small size.
In this example we would use library GSON
.
####Why GSON?
- Good speed when we work with large data.
- Wokr whithout annotatioans and metadata. We simply can say - i want this object convert to JSON.
- We can handle order of dumping.
- Work with
null
. - Simple to use and work.
####How we can configure it?
We should create GSON Builder
:
Gson gsonBuilder = new GsonBuilder().setPrettyPrinting().create();
When we build our Gson parser - we can call another methods like:
- excludeFieldsWithModifiers()
- setDateFormat()
and etc.
It's very good choice, simple and powerful, without annotatioans and metadata. Just try it.