| < Day Day Up > |
|
Serialization of Java objects means saving the object’s state to a persistent storage—such as a file stream or a memory stream buffer—for later use. Serialized objects can be retrieved later from the stream and constructed to the initial state, that the object had when it was serialized. By default, all the objects in a Java program are transient in nature, which means that after all the references to the objects go out of scope, those objects become a target of the garbage collector, to be cleared off the memory the next time the Java Virtual Machine runs the garbage collector. However, serialized objects are stored to a persistent storage and can be rebuilt when needed. Java is one of the few languages that have provided built-in support for object serialization and garbage collection. It may be recalled from Chapter 6, Desktop Application Development, that Borland’s Kylix development tool also provided this feature in its CLX component library supporting the Delphi and C++ languages, imparting the ability to the components to save their state to persistent storage—and also the ability of a component to destroy all the components it owns when it destroys itself—because these features are not supported in these languages by default.
Typically, object serialization is used for transporting objects across a network. This is one of the requirements in building effective data marshalling software, which in turn forms the basis of distributed applications. When an object is serialized to an output stream, the information necessary to identify the class of the object and its current state (with the exception of static and transient fields) is stored to the stream to enable the corresponding input stream to reconstruct the object. For objects to be serialized, the class must implement the java.io.Serializable interface. Classes that implement the Serializable interface but whose super class does not implement the Serializable interface can also be serialized. However, only fields of the serializable sub class will be serialized to the stream. The class should have a constructor that takes no arguments and is capable of initializing the super class fields. The Serializable interface does not have any methods to be implemented; rather, it identifies (and tags) the object as serializable to enable the stream objects to distinguish between objects that should be serialized and those that should not be serialized. Typically, objects are serialized by invoking the writeObject method of the java.io.ObjectOutputStream class and passing the object to be serialized as the single argument to the method. Similarly, the readObject method of the java.io.ObjectInputStream class will retrieve objects from the stream and reconstruct them to their state before serializing them. Any number of objects can be serialized to the stream sequentially; however, they should also be deserialized back in the same order. Other than the writeObject method, the ObjectOutputStream class also provides methods such as writeInt, writeDouble, writeLong, and so on
to serialize the primitive data types. These methods are inherited from the java.io.DataOutput interface, which is implemented by the ObjectOutputStream class. With respect to serialization, the stream objects that contain the serialized objects may be considered as object containers. As specified earlier, data members of a serializable class—which are identified by the transient keyword—or the static data members will not be serialized, and all other data members are serialized by default. However, the data members that should be serialized can also be specified explicitly by declaring a special field named serialPersistentFields, which is an array of ObjectStreamField objects, each of these objects representing a field that should be serialized. The following code snippet will explain how this can be done. The example shows two fields; however, more fields can be added in the definition of the serialPersistentFields field.
import java.io.*; import java.lang.*; import java.util.*; class MyClass implements Serializable { Vector records; String recordSetName; private static final ObjectStreamField[] serialPersistentFields = {new ObjectStreamField("records", Vector.class), new ObjectStreamField("recordSetName", String.class)}; }
When a serialized object contains fields that represent references to other objects, the process of serialization is performed on all such objects transitively in order to serialize the object in its entirety, provided the referenced objects are also serializable. If the referenced objects are not serializable, a runtime exception of type NotSerializableException will be raised, and it is the responsibility of the developer to recover the stream to an acceptable state. However, a serializable object can implement its own writeObject method to have control of serializing its contents. For example, if a member of the class is a reference to an object that is not serializable, then we may implement the writeObject method on the class and serialize the member object’s contents, or we may write the custom writeObject method to exclude one or more data members from being serialized. The custom writeObject method should be implemented with the following signature, and before performing any custom serialization task, the defaultWriteObject or writeFields method of the ObjectOutputStream class should be called.
private void writeObject(java.io.ObjectOutputStream stream) throws java.io.IOException;
When an object is being serialized by calling the writeObject method of the ObjectOutputStream class, it attempts to execute the writeObject method of the class, if one is implemented. If one is not implemented, its own defaultWriteObject method will be executed. Along with the custom writeObject method, the custom readObject method should also be implemented to facilitate reading (and rebuilding) the object from the custom-serialized object. Listing 7.1 displays a simple Java class whose object is serialized through the default writeObject method of the ObjectOutputStream class to a local disk file in the main function, and Listing 7.2 displays a modified version of the previous Java class that serializes and deserializes only some of the fields through the implementation of private methods writeObject and readObject, respectively. As mentioned earlier, this approach will make sure that the class’s own methods are called automatically when the corresponding methods are called on the respective stream objects. In the custom implementation of writeObject, if we call the defaultWriteObject method of the output stream, then the stream will perform the default serialization (of all the serializable fields). Similarly, in the custom implementation of readObject, if we call the defaultReadObject method of the input stream, then the stream will perform the default deserialization. Because this class is written to demonstrate custom serialization, these statements are written and commented. If the comment markers are removed, then the object will be completely serialized, and the impact of custom serialization cannot be noticed. Listing 7.3 displays how the two serialized objects (the default serialized object and the custom serialized object) are deserialized. It should be noted that in the custom serialized object, only two fields (firstName and lastName) are serialized, and hence an attempt to read the middleName and currentTime fields will return null objects. Listing 7.1, 7.2, and 7.3 are available on the accompanying CD-ROM.
Listing 7.1: ObjSerialize.java
package serialize; import java.io.*; import java.util.*; public class ObjSerialize implements Serializable { private String firstName; private String middleName; private String lastName; private Calendar currentTime; public ObjSerialize() { } public void setFirstName(String fName) { firstName = fName; } public void setMiddleName(String mName) { middleName = mName; } public void setLastName(String lName) { lastName = lName; } public void setCurrentTime(Calendar cTime) { currentTime = cTime; } public String getFirstName() { return firstName; } public String getMiddleName() { return middleName; } public String getLastName() { return lastName; } public Calendar getCurrentTime() { return currentTime; } public static void main(String[] args) { ObjSerialize objSerialize1 = new ObjSerialize(); objSerialize1.setFirstName("Satya"); objSerialize1.setMiddleName("Sai"); objSerialize1.setLastName("Kolachina"); objSerialize1.setCurrentTime(Calendar.getInstance()); try { FileOutputStream fstr = new FileOutputStream("SerializedObject.ser"); ObjectOutputStream ostr = new ObjectOutputStream(fstr); ostr.writeObject(objSerialize1); System.out.println("Object is successfully serialized"); } catch (IOException ioe) { ioe.printStackTrace(); } } }
Listing 7.2: ObjSelfSerialize.java
package serialize; import java.io.*; import java.util.*; public class ObjSelfSerialize implements Serializable { private String firstName; private String middleName; private String lastName; private Calendar currentTime; public ObjSelfSerialize() { } public void setFirstName(String fName) { firstName = fName; } public void setMiddleName(String mName) { middleName = mName; } public void setLastName(String lName) { lastName = lName; } public void setCurrentTime(Calendar cTime) { currentTime = cTime; } public String getFirstName() { return firstName; } public String getMiddleName() { return middleName; } public String getLastName() { return lastName; } public Calendar getCurrentTime() { return currentTime; } private void writeObject(ObjectOutputStream str) throws IOException { try { //str.defaultWriteObject(); str.writeObject(firstName); str.writeObject(lastName); } catch (IOException ioe) { ioe.printStackTrace(); } } private void readObject(ObjectInputStream str) throws IOException, ClassNotFoundException { try { //str.defaultReadObject(); firstName = (String)str.readObject(); lastName = (String)str.readObject(); } catch (IOException ioe) { ioe.printStackTrace(); } } public static void main(String[] args) { try { ObjSelfSerialize objSelfSerialize1 = new ObjSelfSerialize(); objSelfSerialize1.setFirstName("Satya"); objSelfSerialize1.setMiddleName("Sai"); objSelfSerialize1.setLastName("Kolachina"); objSelfSerialize1.setCurrentTime(Calendar.getInstance()); FileOutputStream fstr = new FileOutputStream("SelfSerializedObject.ser"); ObjectOutputStream ostr = new ObjectOutputStream(fstr); ostr.writeObject(objSelfSerialize1); System.out.println("Object is successfully Self-serialized"); } catch (IOException ioe) { ioe.printStackTrace(); } } }
Listing 7.3: ObjDeserialize.java
package serialize; import java.io.*; import java.util.*; public class ObjDeserialize { public ObjDeserialize() { } public static void main(String[] args) { ObjDeserialize objDeserialize1 = new ObjDeserialize(); try { FileInputStream fstr = new FileInputStream("SerializedObject.ser"); ObjectInputStream istr = new ObjectInputStream(fstr); ObjSerialize objSer = (ObjSerialize) istr.readObject(); System.out.println("First Name : "+objSer.getFirstName()); System.out.println("Middle Name : "+objSer.getMiddleName()); System.out.println("Last Name : " + objSer.getLastName()); System.out.println("Current Time : " + objSer.getCurrentTime().getTime()); System.out.println("Object is successfully deserialized"); } catch (ClassNotFoundException ioe) { ioe.printStackTrace(); } catch (IOException ioe) { ioe.printStackTrace(); } try { FileInputStream fstr = new FileInputStream("SelfSerializedObject.ser"); ObjectInputStream istr = new ObjectInputStream(fstr); ObjSelfSerialize objSer = (ObjSelfSerialize) istr.readObject(); System.out.println("First Name : "+objSer.getFirstName()); System.out.println("Last Name : "+objSer.getLastName()); System.out.println("Middle Name : " + objSer.getMiddleName()); try { System.out.println("Current Time : " + objSer.getCurrentTime().getTime()); } catch (NullPointerException npe) { System.out.println("Current time is null"); } System.out.println("Object is successfully deserialized"); } catch (ClassNotFoundException ioe) { ioe.printStackTrace(); } catch (IOException ioe) { ioe.printStackTrace(); } } }
| < Day Day Up > |
|