-
Notifications
You must be signed in to change notification settings - Fork 3
Serialization
##Binary Serialization
A lot of people think that serialization is quite simple - all that we do is implement java.io.Serializable
interface.
Yes, in simple - it's all that we need, but what about different finer points? How many ways are there to serialize object? One? Two? The right answer is - two ways! We can:
- implements
java.io.Serializable
- implements
Externalizable
Let's talk about it more detail!
Standart serialization in Java works through Reflection API, class puzzles out for the fields and writes to the output. And ofcourse if we use reflection - it's not optimal. And another one - when we use standart serialization we don't call constructor of the class in deserialization, we reserve memory for the class and after that all class fields fill by values from stream(input). We don't call constructor!
And what about parent class? If our class is serializable, what about parent class? Parent class isn't serializable!
When we deserialize our class - we call constructor without parameters! And we don't call constructor of class which we try to deserialize.
If we haven't got this constructor - we catch an error.
After our example in code - we have class with parameter i
which we recieve after deserialization and other parameters create default constructor.
And another one interesting moment: We implement Serializable interface and put two methods to our class:
private void readObject(java.io.ObjectInputStream stream) throws IOException, ClassNotFoundException;
private void writeObject(java.io.ObjectOutputStream stream) throws IOException;
private void readObjectNoData() throws ObjectStreamException;
And when we serialize our object - java calls these methods! But we should implement these methods only if we want special handling during the serialization and deserialization process.
In writeObject, for example:
private void writeObject(java.io.ObjectOutputStream stream) throws IOException {
stream.defaultWriteObject(); // default serialization
//our special serialization
}
In readObject, for example:
private void readObject(java.io.ObjectInputStream stream) throws IOException, ClassNotFoundException {
stream.defaultReadObject(); // default deserialization
//our special deserialization and using setters
}
To sum up:
- If our parent class is Serializable - all children are serializable.
- If child class is Serializable, but parent isn't - parent's fileds initialize by default constructor.
-
java.io.Serializable
- is a mark, label, all classes which we want to serialize should implement it. - We can use special handling during serialization and deserialization - just implement special methods in class.
- In Serialization process: our class metadata, parent metadata, patent data , our class data.
- In Deserialization process: parent metadata, our class metadata, our class data, parent data.
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.
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.
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.
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.