Object I/O

Paul Ngugi - Jul 1 - - Dev Community

ObjectInputStream/ObjectOutputStream classes can be used to read/write serializable objects. DataInputStream/DataOutputStream enables you to perform I/O for primitive-type values and strings. ObjectInputStream/ObjectOutputStream enables you to perform I/O for objects in addition to primitive-type values and strings. Since ObjectInputStream/ObjectOutputStream contains all the functions of DataInputStream/DataOutputStream, you can replace DataInputStream/DataOutputStream completely with ObjectInputStream/ObjectOutputStream.

ObjectInputStream extends InputStream and implements ObjectInput and ObjectStreamConstants, as shown in Figure below.

Image description

ObjectInput is a subinterface of DataInput. ObjectStreamConstants contains the constants to support ObjectInputStream/ObjectOutputStream. ObjectOutputStream extends OutputStream and implements ObjectOutput and ObjectStreamConstants, as shown in Figure below. ObjectOutput is a subinterface of DataOutput.

Image description

You can wrap an ObjectInputStream/ObjectOutputStream on any InputStream/OutputStream using the following constructors:

// Create an ObjectInputStream
public ObjectInputStream(InputStream in)

// Create an ObjectOutputStream
public ObjectOutputStream(OutputStream out)

The code below writes student names, scores, and the current date to a file named object.dat.

Image description

An ObjectOutputStream is created to write data into the file object.dat in lines 8. A string, a double value, and an object are written to the file in lines 11–13. To improve performance, you may add a buffer in the stream using the following statement to replace lines 8:

ObjectOutputStream output = new ObjectOutputStream(
new BufferedOutputStream(new FileOutputStream("object.dat")));

Multiple objects or primitives can be written to the stream. The objects must be read back from the corresponding ObjectInputStream with the same types and in the same order as they were written. Java’s safe casting should be used to get the desired type. The code below reads data from object.dat.

Image description

The readObject() method may throw java.lang.ClassNotFoundException, because when the JVM restores an object, it first loads the class for the object if the class has not been loaded. Since ClassNotFoundException is a checked exception, the main method declares to throw it in line 6. An ObjectInputStream is created to read input from object.dat in lines 8. You have to read the data from the file in the same order and format as they were written to the file. A string, a double value, and an object are read in lines 11–13. Since readObject() returns an Object, it is cast into Date and assigned to a Date variable in line 13.

The Serializable Interface

Not every object can be written to an output stream. Objects that can be so written are said to be serializable. A serializable object is an instance of the java.io.Serializable interface, so the object’s class must implement Serializable.

The Serializable interface is a marker interface. Since it has no methods, you don’t need to add additional code in your class that implements Serializable. Implementing this interface enables the Java serialization mechanism to automate the process of storing objects and arrays.

To appreciate this automation feature, consider what you otherwise need to do in order to store an object. Suppose you wish to store an ArrayList object. To do this you need to store all the elements in the list. Each element is an object that may contain other objects. As you can see, this would be a very tedious process. Fortunately, you don’t have to go through it manually. Java provides a built-in mechanism to automate the process of writing objects. This process is referred as object serialization, which is implemented in ObjectOutputStream. In contrast, the process of reading objects is referred as object deserialization, which is implemented in ObjectInputStream.

Many classes in the Java API implement Serializable. All the wrapper classes for primitive type values, java.math.BigInteger, java.math.BigDecimal, java.lang.String, java.lang.StringBuilder, java.lang.StringBuffer, java.util.Date, and java.util.ArrayList implement java.io.Serializable. Attempting to store an object that does not support the Serializable interface would cause a NotSerializableException.

When a serializable object is stored, the class of the object is encoded; this includes the class name and the signature of the class, the values of the object’s instance variables, and the closure of any other objects referenced by the object. The values of the object’s static variables are not stored.

Nonserializable fields

If an object is an instance of Serializable but contains nonserializable instance data fields, can it be serialized? The answer is no. To enable the object to be serialized, mark these data fields with the transient keyword to tell the JVM to ignore them when writing the object to an object stream. Consider the following class:

public class C implements java.io.Serializable {
private int v1;
private static double v2;
private transient A v3 = new A();
}
class A { } // A is not serializable

When an object of the C class is serialized, only variable v1 is serialized. Variable v2 is not serialized because it is a static variable, and variable v3 is not serialized because it is marked transient. If v3 were not marked transient, a java.io.NotSerializableException would occur.

Duplicate objects

If an object is written to an object stream more than once, will it be stored in multiple copies? No, it will not. When an object is written for the first time, a serial number is created for it. The JVM writes the complete contents of the object along with the serial number into the object stream. After the first time, only the serial number is stored if the same object is written again. When the objects are read back, their references are the same since only one object is actually created in the memory.

Serializing Arrays

An array is serializable if all its elements are serializable. An entire array can be saved into a file using writeObject and later can be restored using readObject. The code below stores an array of five int values and an array of three strings and reads them back to display on the console.

Image description

Lines 14 and 15 write two arrays into file array.dat. Lines 21 and 22 read two arrays back in the same order they were written. Since readObject() returns Object, casting is used to cast the objects into int[] and String[].

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .