Connecting Tech Pros Worldwide Forums | Help | Site Map

Author Madhoriya22: Serialization

JosAH's Avatar
Expert
 
Join Date: Mar 2007
Posts: 10,611
#1   Sep 5 '07
Introduction

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
  1. public class MyClass implements Serializable {
  2. ........
  3. ........//your code goes here
  4. }
  5.  
(ii) By implementing the Externalizable interface:



Expand|Select|Wrap|Line Numbers
  1. public class MyClass implements Externalizable {
  2. ........
  3. ........//your code goes here
  4. }
  5.  
The interfaces

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
  1. import java.io.*;
  2. public class SerializeClass implements Serializable {
  3.  
  4.     private double i = 0;
  5.     private String str = null;
  6.     SerializeClass() {
  7.         i = 49.90;
  8.         str = "MySerializeClass Object";
  9.     }
  10.  
  11.     public double getDoubleValue(){
  12.         return i;
  13.     }
  14.  
  15.     public String getStringValue(){
  16.         return str;
  17.     }
  18. }
  19.  
Serializabe interface is part of the Java IO package. A class that implements Serializabe interface must be declare as public (the reason for this can be easily understood). All data members of that class must be primitive, or Serializable objects themselves.We can easily save objects of this class to a stream like:


Expand|Select|Wrap|Line Numbers
  1. String path = "e:/folder/file"//set path u want.
  2. FileOutputStream fos = new FileOutputStream(path);//Creating a stream for writing.
  3. ObjectOutputStream oos = new ObjectOutputStream(fos);//Creating a object to write to the file
  4.  
  5. SerializeClass serializeObject = new SerializeClass();//SerializeClass Object
  6. oos.writeObject(serializeObject);
  7. oos.flush();//forces the data to get written to the stream.
  8.  
As the object is written to the file, Later u can get this object back from the file and use the values attached to that object. It can be done like this:


Expand|Select|Wrap|Line Numbers
  1. FileInputStream fis = new FileInputStream(path);
  2. ObjectInputStream ois = new ObjectInputStream(fis);
  3.  
  4. serialObject = (SerializeClass)ois.readObject();
  5.  
You can use this object to access attached values:-

Expand|Select|Wrap|Line Numbers
  1. System.out.println(serialObject.getStringValue());    
  2. System.out.println(serialObject.getDoubleValue());
  3.  
Example for Externalizable interface:

Expand|Select|Wrap|Line Numbers
  1. import java.io.*;
  2. public class ExternalizeClass implements Externalizable {
  3.  
  4.     private double i = 0; 
  5.     private String str = null;
  6.  
  7.     ExternalizeClass() {
  8.         i = 99.0;
  9.         str = "MyExternalize Object";
  10.     }
  11.  
  12.     public double getDoubleValue(){
  13.         return i;
  14.     }
  15.  
  16.     public String getStringValue(){
  17.         return str;
  18.     }
  19.  
  20.     //we must define these methods
  21.     public void writeExternal(ObjectOutput out) throws IOException {
  22.         out.writeDouble(this.i);
  23.         out.writeObject(this.str);
  24.     }
  25.  
  26.     public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
  27.         this.i = in.readDouble();
  28.         this.str = (String)in.readObject();
  29.     }
  30. }
  31.  
Same as we did with a SerializeClass object we can do with ExternalizeClass object to write and read from the file. Here we have to use writeExternal() and readExternal() methods.

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
  1. static final long serialVersionUID = 2L;
  2.  
This declaration shows that version 2 is as far back as this class can go. It is not compatible with an object written by version 1 of the class. It it encounters an version 1 object it will throw an InvalidClassException.

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
  1. ObjectSteamClass myObject = ObjectStreamClass.lookUp(class.forName("Myclass");
  2. long SUID = myObject.getSerialVersionUID();
  3.  
What do these two lines do? They query the virtual machine for information
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
  1. static final long serialVersionUID = 1L;
  2.  
Add two new variables like.


Expand|Select|Wrap|Line Numbers
  1. private double j = 77.7; //&
  2. private String str1 = "New String";
  3.  
Try to access these variables through serialize class object. It will give you the default value of these objects i.e. 0 for double and null for String and no exception will be thrown. But if you change the varialbe serialVersionUID to
2L, it will not read those two new variables and will throw an InvalidClassException.



Reply


Similar Java bytes