SCJP Study Guide:
API Contents


Printer-friendly version Printer-friendly version | Send this 
article to a friend Mail this to a friend


Previous Next vertical dots separating previous/next from contents/index/pdf Contents
XyzWs Study Guide: SCJP: ObjectInputStream

ObjectInputStream


An ObjectInputStream deserializes primitive data and objects previously written using an ObjectOutputStream. The objects and other data must have been written by an ObjectOutputStream. ObjectOutputStream and ObjectInputStream can provide the ability to save the state information of components on some persistent (permanent) storage medium so that it can be restored when the application is run later, when they are used in conjunction with FileInputStream and FileOutputStream. The classes can also be used with socket streams to pass objects across the network.

The ObjectInputStream constructor requires an InputStream.The constructor calls readStreamHeader to read and verifies the header and version written by the corresponding ObjectOutputStream.writeStreamHeader method.

  • public ObjectInputStream(InputStream in) throws IOException creates an ObjectInputStream that reads from the specified InputStream. A serialization stream header is read from the stream and verified. This constructor will block until the corresponding ObjectOutputStream has written and flushed the header. If a security manager is installed, this constructor will check for the "enableSubclassImplementation" SerializablePermission when invoked directly or indirectly by the constructor of a subclass which overrides the ObjectInputStream.readFields or ObjectInputStream.readUnshared methods. Throws:

    • A StreamCorruptedException is thrown, if the stream header is incorrect.
    • An IOException is thrown, if an I/O error occurs while reading stream header.
    • A SecurityException is thrown, if untrusted subclass illegally overrides security-sensitive methods.
    • A NullPointerException is thrown, if in is null.

ObjectInputStream is used to recover those objects previously serialized by an ObjectOutputStream. Other uses include passing objects between hosts using a socket stream or for marshaling and unmarshaling arguments and parameters in a remote communication system.

ObjectInputStream ensures that the types of all objects in the graph created from the stream match the classes present in the Java Virtual Machine. Classes are loaded as required using the standard mechanisms.

Ony objects that are instances of classes that implement the Serializable or Externalizable interfaces can be deserialized from an input stream.

Primitive data types can be read from the stream using the appropriate method on DataInput.

The method readObject is used to read an object from the stream. It deserializes the specified object and traverses its references to other objects recursively to create the complete graph of objects serialized. Objects read from the stream are type checked as they are assigned. Java's safe casting should be used to get the desired type. In Java, strings and arrays are objects and are treated as objects during serialization. When read they need to be cast to the expected type. 

When an object is deserialized in the default deserialization mechanism, the non-static and non-transient fields of the object are restored to the values they had when the object was serialized, including any other objects referenced by the object (except for those objects that do not implement the Serializable interface themselves). Graphs of objects are restored using a reference sharing mechanism. Fields declared as transient or static are IGNORED by the deserialization process. New object instances are always allocated during the deserialization process, to prevent existing objects from being overwritten. Deserialized objects are returned as instances of type Object , so they should be cast to the appropriate type. Strings and arrays are objects in Java, so they are treated as objects during deserialization.

Reading an object is analogous to running the constructors of a new object. Memory is allocated for the object and initialized to zero (NULL). No-arg constructors are invoked for the non-serializable classes and then the fields of the serializable classes are restored from the stream starting with the serializable class closest to java.lang.Object and finishing with the object's most specific class. For example:

  

  try {
// Serialize today's date to a file.
    FileOutputStream f = new FileOutputStream("t.tmp");
    ObjectOutputStream  oos  =  new  ObjectOutputStream(f);
    oos.writeObject("Today");
    oos.writeObject(new Date());
    oos.flush();
    oos.close();
//Deserialize today's date from a file
    FileInputStream fis = new FileInputStream("t.tmp");
    ObjectInputStream ois = new ObjectInputStream(fis);
    int i = ois.readInt();
    String today = (String) ois.readObject();
    Date date = (Date) ois.readObject();
    ois.close();
  } catch (Exception e) { 
   ....
  }
  

Classes control how they are serialized by implementing either the java.io.Serializable or java.io.Externalizable interfaces.

Implementing the Serializable interface allows object serialization to save and restore the entire state of the object and it allows classes to evolve between the time the stream is written and the time it is read. It automatically traverses references between objects, saving and restoring entire graphs.

 

Classes that have transient instance variables may require special handling to reconstruct the values of these variables when objects are deserialized. Special handling may also be necessary to correctly deserialize objects that were serialized with a different version of their class than is in use when they are deserialized. Serializable classes that require special handling during the serialization and deserialization process should implement the following methods:

 private void writeObject(java.io.ObjectOutputStream stream)
     throws IOException;
 private void readObject(java.io.ObjectInputStream stream)
     throws IOException, ClassNotFoundException; 
 private void readObjectNoData() 
     throws ObjectStreamException;

The writeObject() method is responsible for writing the state of the object for the particular class so that it can be restored by readObject(). The readObject() method registers an object validation callback by calling registerValidation() as its first action. The readObject method is responsible for reading and restoring the state of the object for its particular class using data written to the stream by the corresponding writeObject method. State is restored by reading data from the ObjectInputStream for the individual fields and making assignments to the appropriate fields of the object.

The readObject() method doesn't need to handle reading the state for the object's superclass or any of its subclasses except in the case where the superclass doesn't itself implement the Serializable interface. In this case, the nonserializable class must have a no-argument constructor that can be called to initialize its fields, and it is the responsibility of the subclass to restore the state of its superclass.

For an object to handle its own serialization it must implement the readObject method. To maintain the integrity of the class, this method is private to the class and can only be called by the serialization at runtime. This method is invoked when the fields of its class are to be read; it should read the information written by writeObject and make appropriate assignments to the object's fields. If the state of the object cannot be completely restored at the time the object is being read, a validation callback can be requested by calling the registerValidation method.

Serialization does not read or assign values to the fields of any object that does not implement the java.io.Serializable interface. Subclasses of Objects that are not serializable can be serializable. In this case the non-serializable class must have a no-arg constructor to allow its fields to be initialized. In this case it is the responsibility of the subclass to save and restore the state of the non-serializable class. It is frequently the case that the fields of that class are accessible (public, package, or protected) or that there are get and set methods that can be used to restore the state.

Implementing the Externalizable interface allows the object to assume complete control over the contents and format of the object's serialized form. The methods of the Externalizable interface, writeExternal and readExternal, are called to save and restore the objects state. When implemented by a class they can write and read their own state using all of the methods of ObjectOutput and ObjectInput. It is the responsibility of the objects to handle any versioning that occurs.

Enum constants are deserialized differently than ordinary serializable or externalizable objects. The serialized form of an enum constant consists solely of its name; field values of the constant are not transmitted. To deserialize an enum constant, ObjectInputStream reads the constant name from the stream; the deserialized constant is then obtained by calling the static method Enum.valueOf(Class, String) with the enum constant's base type and the received constant name as arguments. Like other serializable or externalizable objects, enum constants can function as the targets of back references appearing subsequently in the serialization stream. The process by which enum constants are deserialized CANNOT be customized: any class-specific readObject, readObjectNoData, and readResolve methods defined by enum types are ignored during deserialization.

A class that inherits the implementation of Serializable prevents itself from being serialized by defining readObject() and writeObject() methods that throw NotSerializableException objects.

If a class needs complete control over the contents and formatting of the serialized form of its objects, it should implement the Externalizable interface.

 

Examples

The following example intends to show that how the ObjectInputStream handles deserializing class whose super class is none-serializable class. You should pay attention about which constructor is called as well as transient and static variable.

import java.io.*;

class NonSerializable {
    private int i;
    private static int j = 10; 
    private transient String str = "abc"; 
    public NonSerializable()
    {
        System.out.println("I am in NonSerializable constructor!");
    }
    
    public void changeValues()
    {
        i = 100;
        j = 200;
        str = "def";
    }
    
    public void printOut() {
        System.out.println("Class NonSerializable:");
        System.out.println("i = " + i);
        System.out.println("j = " + j);
        System.out.println("str = " + str);
    }
}


public class ObjectInputStreamSample extends NonSerializable implements Serializable {

    private int m;
    private static int  n= 10; 
    private transient String str = "hello"; 
    private String str1;
    public ObjectInputStreamSample()
    {
        System.out.println("I am in ObjectInputStreamSample constructor!");
    }
    
    public void changeValues(){
        m = 100;
        n = 200;
        str = "transient";
        str1 = "non-transient";
        super.changeValues();
    }
    
    public void printOut() {
        System.out.println("Class ObjectInputStreamSample:");
        System.out.println("m = " + m);
        System.out.println("n = " + n);
        System.out.println("str = " + str);
        System.out.println("str1 = " + str1);
        super.printOut();
    }
    
    public static void main(String[] args) throws IOException, ClassNotFoundException {
        
        ObjectInputStreamSample a = new ObjectInputStreamSample();
        a.changeValues();
        System.out.println("Before serialization:");
        a.printOut(); 
        ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("myTest.ser")); 
        oos.writeObject(a); 
        oos.flush(); 
        oos.close();
        
        ObjectInputStream ois = new ObjectInputStream(new FileInputStream("myTest.ser")); 
        ObjectInputStreamSample b = (ObjectInputStreamSample)ois.readObject(); 
        System.out.println("After serialization:");
        b.printOut(); 
    
    }
}
  

The output is

I am in NonSerializable constructor!
I am in ObjectInputStreamSample constructor!
Before serialization:
Class ObjectInputStreamSample:
m = 100
n = 200
str = transient
str1 = non-transient
Class NonSerializable:
i = 100
j = 200
str = def
I am in NonSerializable constructor!
After serialization:
Class ObjectInputStreamSample:
m = 100
n = 200
str = null
str1 = non-transient
Class NonSerializable:
i = 0
j = 200
str = abc

Previous Next vertical dots separating previous/next from contents/index/pdf Contents

  |   |