Java Object Serialization


Overview

You can write an object of any kind to a stream. This could be to a disk file or to a Server via a Socket, etc.

Also, you can read an object of any kind from a stream.

The object written can contain primitives or references to other objects, which themselves may contain primitives and references to still other objects.  The write operation will output them (i.e. all the data values and methods in a format that will allow them to be read back later and completely reconstructed as they were before they were written.

You will use the ObjectOutputStream and ObjectInputStream classes.  The constructor for the first takes an OutputStream argument; the constructor for the second takes an InputStream argument.  You can open files to get these, or in the case of client-server Socket communications, you can use methods of the Socket class to obtain these I/O streams.  Note: socket has previously been created.

ObjectOutputStream objOut = new ObjectOutputStream(socket.getOutputStream()); 
ObjectInputstream  objIn  = new ObjectInputStream(socket.getInputStream()); 
Some client-server programs appear not to work if the ObjectInputStream is created before the ObjectOutputStream.  This may be because the ObjectInputStream constructor blocks while reading a header from the stream source.  If that source is a Socket and had not yet created a corresponding OuputStream, your program may hang. If your program appears to hang on creation of the Object I/O streams, try the order shown above on both sides.
 

Note in the example below the Exceptions you need to catch.

Then to read an object:

person = (Person) objIn.readObject(); 

Note that the read-in object needs to be properly type-cast.  Usually you will know what kind of an object it is. If it might be one of several classes, you can use instance of to test what kind of a class it is – read it into Object temp and then test temp. The type-cast can be to the base class of a polymorphic hierarchy or to an interface type where this would be valid and appropriate.

To write an object:
objOut.writeObject(p); 
where p is an object of any kind.  After you issue this call, you should make sure it is written right away (it may be buffered in some cases) by calling
objOut.flush(); 
The objects read and written (and all of the objects they contain - i.e. refer to) must implement Serializable. This is an interface that has no methods – it’s just a marker. Most Java library classes do implement Serializable.  In order for you to create a class that is serializable, just add the “implements Serializable” clause to the class.  See the Person example below.

When you serialize an object, all its variables are written out, as are all included objects with references (for example, the String and List in the Person class shown below).  When they are read in, they are re-assembled as before.

If there is some data you don’t want serialized (for example the current time of day or the current mouse position), then place the keyword transient in front of the data declaration.

Finally, there is a non-obvious problem in some cases.  If you want to write the same object several times (directly or because it’s referenced in the object you will write) and if the state of this object or these included objects changes between writes, you won’t get the changes written out unless you call
objOut.reset(); 
The reason is the object stream caches the first version of each object that it writes and just reuses it unless you reset the stream.  As an example, if the contents of the List referred to in the Person object changes between writes (of the same Person object), you’d have to do a reset.

Example

//
// Part of a class that does object reading via a Socket
// The ObjectOutputStream is created but not used in this code fragment.
//

protected Socket             socket;
protected ObjectInputStream  objIn;
protected ObjectOutputStream objOut; 

//create the socket...

try
  {   
  objOut    = new ObjectOutputStream(
socket.getOutputStream()); 
  objIn     = new ObjectInputStream(
socket.getInputStream());
  }

catch (StreamCorruptedException sce)
  {
  System.out.println("Stream Corrupted Exception" + sce.getMessage());
  }
catch (OptionalDataException ode)
  {
  System.out.println("Optional Data Exception" + ode.getMessage());
  }
catch (IOException ioe)
  {
  System.out.println("i/o exception creating stream" + ioe.getMessage());
  }                         

while (true)
  {
  // repeatedly read a Person object from client       
  try
    {
    person = (Person) objIn.readObject();
    }
  catch (EOFException eof)
    {
    System.out.println("eof encountered" + eof.getMessage());
    break; 
    } 
  catch (OptionalDataException ode)
    {
    System.out.println("OptionalDataException" + ode.getMessage());
    }
  catch (IOException ioe)
    {                  
    System.out.println("IOException on read object");
    System.out.println(ioe.getMessage());
    System.out.println(ioe.toString());
    }
  catch (ClassNotFoundException cnf)
    {
    System.out.println("ClassNotFoundException");
    }
 
    }

//
// The Person class
//

public class Person implements Serializable
  {
  protected String name;
  protected int id;
  protected List aList;  

  public Person()
    {
    name = "none";
    id = 0;
    } 

  public Person(String s, int n, List l)
    {
    name = s;
    id = n;
    aList = l;
    } 

  public String getName()
    {
    return name;
    }   

  public int getID()
    {
    return id;
    }     

  public List getList()
    {
    return aList;
    } 

  public void modPerson()
    {
    name = name.concat("<-- changed");
    id++;
    } 
}