Upon hearing the word, "Serialization", the first question which comes to mind is ...
"What is Serialization?" We know that we can create resusable objects in Java. But the lifetime of those objects lasts only as long as the Java virtual machine is running. Once we close the JVM, we lose all those objects.
What if someone wants objects to be available after JVM restarts? This is where Serialization comes into play.
Serialization is the process of storing an object's current state into a stream. This stream functions as a container for the object. Later, we can restore an equivalent object from that stream.
Serialization or Externalization
The stream container can be of one of two types:
- Transient (RAM-based) or
- Persistent (disk-based).
Transient storage is used to create objects for transmission from one computer to another.
Persistent storage allows the usage of the objects after the current session is finished.
There are two ways to serialize class objects:
(i) By implementing the Serializable interface:
Expand|Select|Wrap|Line Numbers
- public class MyClass implements Serializable {
- ........
- ........//your code goes here
- }
Expand|Select|Wrap|Line Numbers
- public class MyClass implements Externalizable {
- ........
- ........//your code goes here
- }
Now some important facts about the Serializable interface:
- The Serializable interface itself don't have any methods. Instead, when a class implements Serializable, it tells the JVM that this class can be stored using a persistent stream container.
- This interface relies on Java's runtime default mechanism to save an object's state.
- Writing an object to the stream is done via the writeObject() method of class ObjectOutputStream (or in the objectOutput interface).
- For writing primitive values, we can use the write<datatype>() method.
- Reading the object is done via the readObject() method of class ObjectInputStream.
- All subclasses of a serialized class are themselves serialized.
Some important facts about the Externalizable interface:
- Unlike the Serializable interface, the Externalizable interface specifies that the implementing class will handle the serialization on its own instead of relying on the default runtime mechanism.
- Note that this means that you are responsible for making sure that all of your data gets saved and in the correct order.
Externalizable declares two methods:
writeExternal()
Used to write your class to a stream container.
As with any class that implements an interface, your class must define this method.
When defining this method, use the ObjectOutputStream class and its methods to facilitate writing the stream.
readExternal()
Used to read your serialized class from a stream container.
As with any class that implements an interface, your class must define this method.
When defining this method, use the ObjectInputStream class and its methods to facilitate reading from the stream.
Now time to understand all these stuff through some examples. Below given class is a example of Serialize class...
Expand|Select|Wrap|Line Numbers
- import java.io.*;
- public class SerializeClass implements Serializable {
- private double i = 0;
- private String str = null;
- SerializeClass() {
- i = 49.90;
- str = "MySerializeClass Object";
- }
- public double getDoubleValue(){
- return i;
- }
- public String getStringValue(){
- return str;
- }
- }
Expand|Select|Wrap|Line Numbers
- String path = "e:/folder/file"//set path u want.
- FileOutputStream fos = new FileOutputStream(path);//Creating a stream for writing.
- ObjectOutputStream oos = new ObjectOutputStream(fos);//Creating a object to write to the file
- SerializeClass serializeObject = new SerializeClass();//SerializeClass Object
- oos.writeObject(serializeObject);
- oos.flush();//forces the data to get written to the stream.
Expand|Select|Wrap|Line Numbers
- FileInputStream fis = new FileInputStream(path);
- ObjectInputStream ois = new ObjectInputStream(fis);
- serialObject = (SerializeClass)ois.readObject();
Expand|Select|Wrap|Line Numbers
- System.out.println(serialObject.getStringValue());
- System.out.println(serialObject.getDoubleValue());
Expand|Select|Wrap|Line Numbers
- import java.io.*;
- public class ExternalizeClass implements Externalizable {
- private double i = 0;
- private String str = null;
- ExternalizeClass() {
- i = 99.0;
- str = "MyExternalize Object";
- }
- public double getDoubleValue(){
- return i;
- }
- public String getStringValue(){
- return str;
- }
- //we must define these methods
- public void writeExternal(ObjectOutput out) throws IOException {
- out.writeDouble(this.i);
- out.writeObject(this.str);
- }
- public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
- this.i = in.readDouble();
- this.str = (String)in.readObject();
- }
- }
We have learned how to write and read a serialize object to the stream. If you want to prevent certain fields from being stored in the serialize object, then what? It can be done so easily by placing keyword transient before the datatype in the variable declaration. static fields are not serialized (written out), and so cannot be serialized (read back in).
Another way of doing the above task is to override writeObject() and readObject() methods. In definitions of these methods you can control which field is not to be written and read.
For the Externalize interface, since both readExternal() and writeExternal() methods must be declared as public this increases the risk that a object could use them to determine the state of an serialize object. So you should be careful while saving object data with this interface.
the serialVersionUID
What happens when an object has been stored for so long, that upon restoration
it finds that its format has been superseded by a new, different version of the class? The reading stream is accounting for any such diffrences. The intent is the newer version of a java class should be able to interoperate with the older representations of the same class as long there have not been certain changes in the class structure.
So either at runtime or at deserialize time we check for backward compatibility.
Changes to classes may specified using Version number. A specific class
variable, serialVersionUID (representing the stream unique identifier, or SUID) may be used to specify the earliest version of the class that can be deserialized. The SUID is declared as follows:
Expand|Select|Wrap|Line Numbers
- static final long serialVersionUID = 2L;
If you don't define a SUID explicitly, a default SUID will be generated. This default
SUID is a hash, or unique numeric value, which is computed using the class
name, interfaces, fields and methods. How can you obtain the SUID for a class at
runtime to determine compatibility? You can get it like this:
Expand|Select|Wrap|Line Numbers
- ObjectSteamClass myObject = ObjectStreamClass.lookUp(class.forName("Myclass");
- long SUID = myObject.getSerialVersionUID();
about the class represnted in the stream, using the methods of the class
ObjectStreamClass.
How do you determine what changes between class versions are acceptable?
These are the changes which can affect the restoration of a object :
Deleting a field, or changing it from non-static or non-transient to static or transient, respectively.
Changing the position of classes in a hierarchy.
Changing the data type of a primitive field.
Changing the interface for a class from Serializable to Externalizable (or vice-versa).
while these changes to the class are acceptable:
Adding fields, which will result in default values (based on data type) being assigned to the new fields upon restoration.
Adding classes will still allow an object of the added class to be created, since the class structure information is included in the stream. However, its fields will be set to the default values.
Adding or removing the writeObject() or readObject() methods.
Changing the access modifier (public, private, etc.) for a field, since it is still possible to assign a value to the field.
Changing a field from static or transient to to non-static or non-transient, respectively.
To understand the above given concept try to add a variable in the Serializable class given above.
Expand|Select|Wrap|Line Numbers
- static final long serialVersionUID = 1L;
Expand|Select|Wrap|Line Numbers
- private double j = 77.7; //&
- private String str1 = "New String";
2L, it will not read those two new variables and will throw an InvalidClassException.