Serialization is the process of saving an object state in a storage medium (such as a file, or a memory buffer) or to transmit it over a network connection in binary form.The object can be restored (Deserialization) at a later time, and even a later location. With persistence, we can move an object from one computer to another, and have it maintain its state.
In general there are three approaches to serialization in java:
- Implement Serializable and use default protocol.
- Implement Serializable and get a chance to modify the default protocol.
- Implement Externalizable and write your own protocol to implement serialization.
The default serialization mechanism by implementing Serializable interface, here you dont have to do much,just implement Seralizable marker interface in your class and invoke readObject and writeObject methods of DataInputStream and DataOutputStream for serialization and deserialization.
Unlike Serializable interface, Externalizable interface is not a marker interface and it provides two methods - writeExternal and readExternal. These methods are implemented by the class to give the class a complete control over the format and contents of the stream for an object and its supertypes.
In this tutorial we will discuss about the default serialization mechanism by implemeting Serializable interface.To learn about Externalization please visit Externalization in Java.
The default serialization mechanism
The default serialization mechanism for an object writes the class of the object, the class signature, and the values of all non-transient and non-static fields. References to other objects (except in transient or static fields) cause those objects to be written also. Multiple references to a single object are encoded using a reference sharing mechanism so that graphs of objects can be restored to the same shape as when the original was written. Serialization transform a graph of Java objects into an array of bytes for storage or transmission.The graph of objects means starting from a single object, until all the objects that can be reached from that object by following instance variables, are also serialized.This includes the super class of the object until it reaches the "Object" class and the same way the super class of the instance variables until it reaches the "Object" class of those variables.
Here are two rules concerning persistent objects:
Rule 1: The object to be persisted must implement the Serializable interface or inherit that implementation from its object hierarchy
Rule 2: The object to be persisted must mark all nonserializable fields transient
We can use writeObject(Object obj) method of java.io.ObjectOutputStream class for writing graph of serializable objects to a byte stream.
Similarly the readObject() method of java.io.ObjectInputStream class can be used to recover those objects previously serialized.
Default Serialization Example
In this example a serializable Employee object is saved and restored using the default serialization mechanism.
package serialize; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.Serializable; public class DefaultSerializeExample { /** * @param args * @throws ClassNotFoundException **/ public static void main(String[] args) throws ClassNotFoundException { ObjectOutputStream out = null; ObjectInputStream in = null; try { out = new ObjectOutputStream(new FileOutputStream("employee.ser")); /** Create one Employee object to serialise **/ Employee emp = new Employee(12345, "Banking"); /** Serialise the object emp and write to file employee.ser **/ out.writeObject(emp); in = new ObjectInputStream(new FileInputStream("employee.ser")); /** Deserialise the object stored in the file employee.ver **/ Employee emp_copy = (Employee) in.readObject(); System.out.println(emp_copy); } catch (IOException e) { e.printStackTrace(); } finally { try { out.close(); in.close(); } catch (IOException e) { e.printStackTrace(); } } } } /** Serializable class **/ class Employee implements Serializable { /** * Generated serialVersionUID for versioning of serialised objects. * **/ private static final long serialVersionUID = -417056492237332874L; int empno; String dept; public Employee(int empno, String dept) { this.empno = empno; this.dept = dept; } public Employee() { } /** Override the toString method to print object fields **/ public String toString() { return (" Employee No =" + empno + " & Department =" + dept); } }
The output will be
Employee No =12345 & Department =Banking
Customize Default Serialization Protocol
Classes that require special handling during the serialization and deserialization process must implement special methods with these exact signatures:
private void writeObject(java.io.ObjectOutputStream out)
throws IOException
private void readObject(java.io.ObjectInputStream in)
throws IOException, ClassNotFoundException;
Notice that both methods are declared private and ofcourse they must be declared private, proving that neither method is inherited and overridden or overloaded. The virtual machine will automatically check to see if either method is declared during serialization and deserialization.If available the custom readObject(ObjectInputStream) and writeObject(ObjectOutputStream) will be used else use the default serialization mechanism. The virtual machine can call private methods of your class whenever it wants but no other objects can.
You can write your custom code inside these two methods and customize the standard behaviour of serialization.
private void writwObject(ObjectInputStream in)
throws IOException{
in.defaultWriteObject(); //Serialize the component in the usual way.
/* Write your custom code here */
}
This method calls the defaultWriteObject() method of the ObjectOutputStream to serialize the object as normal, and then takes care of the post-processing it needs to perform.Similarly
private void readObject(ObjectInputStream in)
throws IOException, ClassNotFoundException {
in.defaultReadObject(); // Deserialize the component in the usual way.
/* Write your custom code here */
}
This method calls the defaultReadObject() method of the ObjectInputStream to deserialize the object as normal, and then takes care of the post-processing it needs to perform.
Let us see one example:
Custom Serialization Example:
In this example we are declaring writeObject and readObject methods inside the serializable sub class Employee for explicitly saving and restoring the non-serializable parent class field 'empName' .
package serialize; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.Serializable; public class CustomSerialization { /** * @param args * @throws ClassNotFoundException **/ public static void main(String[] args) throws ClassNotFoundException { ObjectOutputStream out = null; ObjectInputStream in = null; try { out = new ObjectOutputStream(new FileOutputStream("employee.ser")); /** Create one Employee object to serialise **/ Employee emp = new Employee("John", 12345, "Banking"); /** Serialise the object emp and write to file employee.ser **/ out.writeObject(emp); in = new ObjectInputStream(new FileInputStream("employee.ser")); /** Deserialise the object stored in the file employee.ver **/ Employee emp_copy = (Employee) in.readObject(); System.out.println(emp_copy); } catch (IOException e) { e.printStackTrace(); } finally { try { out.close(); in.close(); } catch (IOException e) { e.printStackTrace(); } } } } /** Non-serializable parent class **/ class Person { String empName; /** * mandatory public non-arg constructor. It will be invoked during * deserialization process. **/ public Person() { System.out.println("Deserialization:Invoking non-arg Person() constructor"); } public Person(String empName) { this.empName = empName; } /** Override the toString method to print object fields **/ public String toString() { return ("Employee Name =" + empName); } } /** Serializable sub class **/ class Employee extends Person implements Serializable { /** * Generated serialVersionUID for versioning of serialised objects. * **/ private static final long serialVersionUID = -417056492237332874L; int empno; String dept; public Employee(String empName, int empno, String dept) { super(empName); this.empno = empno; this.dept = dept; } public Employee() { } /** Override the toString method to print object fields **/ public String toString() { return ("Employee Name =" + empName + "Employee No =" + empno + " & Department =" + dept); } /** * Declared to customize the deserialization process. The nonserialized * super class field 'empName' will be restored explicitly. **/ private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException { // Restore the sub class fields. in.defaultReadObject(); System.out .println("Deserialization: Restoring Non-Serializable parent class field explicitly."); // explicitly do the restoring for super class field. empName = in.readUTF(); } /** * Declared to customize the serialization process. The nonserialized super * class field 'empName' will be saved explicitly. **/ private void writeObject(ObjectOutputStream out) throws IOException { // Save the sub class fields. out.defaultWriteObject(); System.out .println("Serialization: Saving Non-Serializable parent class field explicitly."); // explicitly do the saving for super class field. out.writeUTF(empName); } }
The output will be
Serialization: Saving Non-Serializable parent class field explicitly.
Deserialization: Invoking non-arg Person() constructor
Deserialization: Restoring Non-Serializable parent class field explicitly.
Employee Name =John Employee No =12345 & Department =Banking
You can see that value of the non-serializable parent class field 'empName' saved and restored successfully.
Create Your Own Protocol: the Externalizable Interface
The third option for serialization is create your own protocol with the
Externalizable
interface.To learn more about externalization please visit Externalization in Java.
No comments:
Post a Comment