SDSU CS 696 Emerging Technologies: Java Distributed Computing
Spring Semester, 1999
Serialization
Previous    Lecture Notes Index    Next    
© 1999, All Rights Reserved, SDSU & Roger Whitney
San Diego State University -- This page last updated 23-Feb-99

Contents of Doc 10, Serialization


References


Graphic Java 1.2 Volume I: AWT 3 rd Edition, Geary, Chapter 11 (pp. 453-456)

Java in a NutShell 2nd Ed, Flanagan, chapter 9

Java 1.2 API

Java Object Serialization Specification revision 1.41 JDK 1.2, November 30, 1998 http://java.sun.com/products/jdk/1.2/docs/guide/serialization/spec/serialTOC.doc.html

Object Serialization Examples: http://java.sun.com/products/jdk/1.2/docs/guide/serialization/examples/index.html

See also Object Serialization at: http://java.sun.com/products/jdk/1.2/docs/guide/serialization/index.html

Doc 10, Serialization Slide # 2

Marshaling


Marshaling is process of encoding object to put them on the wire (network)
Unmarshaling is the process of decoding from the wire and placing object in the address space

RMI uses serialization (deserialization) to perform marshaling (Unmarshaling)

All parameters passed by value between client and server are marshaled/unmarshaled

Doc 10, Serialization Slide # 3

Serialization


Serialization allows objects to be converted to a sequence of bytes

The sequence of bytes can be stored in a file, database, sent to a remote machine etc.

The sequence of bytes can be used to recreate the original object


Serializing
Creating the sequence of bytes from an object

Deserializing
Recreating the object from the above generated bytes
Actually creates a new object with the fields containing the same values as the original object
If the fields are also serializable objects then they are also "recreated"


Doc 10, Serialization Slide # 4

Making an Object Serializable


The object's class must implement the interface
java.io.Serializable

Serializable has no methods

Example

import java.io.Serializable;
class Roger implements Serializable
   {
   public int lowBid;
   
   public Roger(int lowBid )
      {
      this.lowBid = lowBid;
      }
   
   public String toString()
      {
      return " " + lowBid;
      }
   }

Doc 10, Serialization Slide # 5
Serializing and Deserializing Objects

The writeObject method of ObjectOutputStream serializes objects

The readObject method of ObjectInputStream deserializes objects

Example
import java.io.*;

class SerializeDeserialize {
   public static void main( String args[] ) throws IOException {
      serialize();
      deserialize();
   }
      
   public static void serialize() throws Exception {
      OutputStream outputFile = 
            new FileOutputStream( "Serialized" );
      ObjectOutputStream cout = 
            new ObjectOutputStream( outputFile );
      cout.writeObject( new Roger( 12));
      cout.close();
   }   
   
   public static void deserialize() throws Exception {
      InputStream inputFile = 
         new FileInputStream( "Serialized" );
      ObjectInputStream cin = 
         new ObjectInputStream( inputFile );
      Roger test = (Roger) cin.readObject();
      System.out.println( test.toString() );
   }
}
Output
 12

Doc 10, Serialization Slide # 6
The Serialized file

0000000 254 355  \0 005   s   r  \0 005   R   o   g   e   r 212 217 260  126355  000005  071562  000005  051157  063545  071212  107660  0000020 262   c   0   ; 255 002  \0 001   I  \0 006   l   o   w   B   i    131143  030073  126402  000001  044400  003154  067567  041151  0000040   d   x   p  \0  \0  \0  \f  062170  070000  000000  006000
0000047
The serialized file is stored in binary. Above is the contents of the file created using JDK 1.2. The file is viewed using the UNIX program "od". You can decode the above file if you read chapter 6 in Java Object Serialization Specification.

Doc 10, Serialization Slide # 7
Some Rules
A Serializable class must:



This can be done in two different ways. The default method or use serialPersistentFields member. The latter will be covered later. Using the default method all fields are identified as serializable by default, unless marked transient. More about that later.


Constructors & Deserialization

When an object is deserialized, no constructors of the object’s class are called.

Doc 10, Serialization Slide # 8
ObjectOutputStream

Methods
annotateClass(Class)
writeByte(int)
close()
writeBytes(String)
defaultWriteObject()
writeChar(int)
drain()
writeChars(String)
enableReplaceObject(boolean)
writeDouble(double)
flush()
writeFields()
putFields()
writeFloat(float)
replaceObject(Object)
writeInt(int)
reset()
writeLong(long)
useProtocolVersion()
writeObject(Object)
write(byte[])
writeObjectOverride()
write(byte[], int, int)
writeShort(int)
write(int)
writeStreamHeader()
writeBoolean(boolean)
writeUTF(String)


public final void writeObject(Object obj) throws IOException

Throws: InvalidClassException
Something is wrong with a class used by serialization.
Throws: NotSerializableException
Some object to be serialized does not implement the java.io.Serializable interface.
Throws: IOException
Any exception thrown by the underlying OutputStream.


Doc 10, Serialization Slide # 9
ObjectInputStream

Methods
available()
readInt()
close()
readLine() deprecated
defaultReadObject()
readLong()
enableResolveObject()
readObject()
read()
readObjectOverride()
read(byte[], int, int)
readShort()
readBoolean()
readStreamHeader()
readByte()
readUnsignedByte()
readChar()
readUnsignedShort()
readDouble()
readUTF()
readFields()
registerValidation()
readFloat()
resolveClass()
readFully(byte[])
resolveObject()
readFully(byte[], int, int)
skipBytes()

public final Object readObject() throws 
   OptionalDataException, 
   ClassNotFoundException, 
   IOException

Throws: ClassNotFoundException
Class of a serialized object cannot be found.

Throws: InvalidClassException
Something is wrong with a class used by serialization.

Throws: StreamCorruptedException
Control information in the stream is inconsistent.

Throws: OptionalDataException
Primitive data was found in the stream instead of objects.

Throws: IOException
Any of the usual Input/Output related exceptions.


Doc 10, Serialization Slide # 10

Nesting Serializable Objects


import java.io.Serializable;
import java.util.Vector;
class Roger implements Serializable
   {
   public int lowBid;
   public int[] allBids = new int[10];
   public Vector bidderNames = new Vector();
   
   public Roger(int lowBid )
      {
      this.lowBid = lowBid;
      bidderNames.addElement( "Bill");
      }
   
   public String toString()
      {
      return " " + lowBid + " " + bidderNames;
      }
   }

Doc 10, Serialization Slide # 11
Multiple Items on One Stream

If more than one object of the same type is put on the stream, the header information for the class is sent only once.

import java.io.*;
class MultipleItems
   {
   public static void main( String args[] ) throws Exception
      {
      serialize();
      deserialize();
      }
      
   public static void serialize() throws IOException
      {
      OutputStream outputFile = 
         new FileOutputStream( "Serialized" );
      ObjectOutputStream cout = 
         new ObjectOutputStream( outputFile );
      cout.writeObject( new Roger( 2 ));
      cout.writeUTF( "Hi Mom" );
      cout.writeObject( "Hi Dad" );
      cout.writeFloat( 2.345F );
      cout.writeObject( new Roger( 3 ));
      cout.close();
      }

Doc 10, Serialization Slide # 12
//Multiple Items - Continued

   public static void deserialize() throws Exception
      {
      InputStream inputFile = 
         new FileInputStream( "Serialized" );
      ObjectInputStream cin = 
            new ObjectInputStream( inputFile );
      System.out.println( cin.readObject() );
      System.out.println( cin.readUTF() );
      System.out.println( cin.readObject() );
      System.out.println( cin.readFloat() );
      System.out.println( cin.readObject() );
      }
   }
Output
2 [Bill]
Hi Mom
Hi Dad
2.345
3 [Bill]

Doc 10, Serialization Slide # 13

Saving and Recovering - A Simple Example


import java.io.*;
import java.util.*;
// Cheap demo class for something to save
public class Student implements Serializable
   {
   String name;
   String address;
   
   public Student( String name, String address )
      {
      this.name = name;
      this.address = address;
      }
   
   public String toString()
      {
      return name + "@" + address + "\n";
      }
   }
   


Doc 10, Serialization Slide # 14
//Saving and Recovering - A Simple Example

public class StudentList implements Serializable
   {
   Vector list = new Vector();
   String storageFileName;
   
   public StudentList( String fileForStorage)
      {
      storageFileName = fileForStorage;
      }
   
   public void addStudent( Student addToList )
      {
      list.addElement( addToList );
      }
      
   public String toString()
      {
      return list.toString();
      }
   
   // Have the list save itself in a file
   public void save() throws IOException
      {
      OutputStream outputFile = 
         new FileOutputStream( storageFileName );
      ObjectOutputStream cout = 
         new ObjectOutputStream( outputFile );
      cout.writeObject( this );
      cout.close();
      }
   

Doc 10, Serialization Slide # 15
//Saving and Recovering - A Simple Example
   //StudentList continued

   // Recover a StudentList from a file
   public static StudentList fromFile( String studentListFile) 
      throws 
         OptionalDataException, 
         ClassNotFoundException, 
         IOException
      {
      InputStream inputFile = 
         new FileInputStream( studentListFile );
      ObjectInputStream cin = 
         new ObjectInputStream( inputFile );
      StudentList recoveredList = 
         (StudentList) cin.readObject();
      cin.close();
      return recoveredList;
      }
   }

Doc 10, Serialization Slide # 16
//Saving and Recovering - A Simple Example
The driver program and Sample output

class Test
   public static void main( String args) throws Exception
      {
      StudentList cs535 = new StudentList( "cs535File");
      cs535.addStudent( 
         new Student( "Roger", "whitney@rohan"));
      cs535.addStudent( 
         new Student( "Sam", "masc1232@rohan"));
      cs535.addStudent( 
         new Student( "Ngu", "masc1111@rohan"));
      cs535.addStudent( 
         new Student( "catMan", "masc43221@rohan"));
      System.out.println( cs535);
      cs535.save();
      
      StudentList recoveredClass = 
            StudentList.fromFile( "cs535File" );
      System.out.println( recoveredClass);
      }   
   }
Output
[Roger@whitney@rohan
, Sam@masc1232@rohan
, Ngu@masc1111@rohan
, catMan@masc43221@rohan
]
[Roger@whitney@rohan
, Sam@masc1232@rohan
, Ngu@masc1111@rohan
, catMan@masc43221@rohan
]

Doc 10, Serialization Slide # 17
Non-Serializable Objects

Some objects should not be serialized

objects that hold system resources


Some values or objects can be recomputed or are not important to serialize

Non-Serializable Fields - transient


If an object has a field that should not be serialized declare the field transient

That field will not be included in the serialization of the object


Doc 10, Serialization Slide # 18
transient Example

import java.io.Serializable;
import java.util.Vector;
class Roger implements Serializable
   {
   private int lowBid;
   private transient float averageBid;
   private int highBid;
   
   public Roger(int lowBid, int highBid )
      {
      this.lowBid = lowBid;
      this.highBid = highBid;
      averageBid = (lowBid + highBid) /2;
      }
   
   public String toString()
      {
      return "Low: " + lowBid + "Ave: " + averageBid;
      }
   }


Doc 10, Serialization Slide # 19
// transient Example - Continued

class TransientExample
   {
   public static void main( String args[] ) throws Exception
      {
      serialize();
      deserialize();
      }
      
   public static void serialize() throws IOException
      {
      OutputStream outputFile = 
         new FileOutputStream( "Serialized" );
      ObjectOutputStream cout = 
         new ObjectOutputStream( outputFile );
      cout.writeObject( new Roger( 1, 5 ));
      cout.close();
      }   
   public static void deserialize() throws Exception
      {
      InputStream inputFile = 
         new FileInputStream( "Serialized" );
      ObjectInputStream cin = 
         new ObjectInputStream( inputFile );
      System.out.println( cin.readObject() );
      }   
   }
Output
Low: 1 Ave: 0.0

Doc 10, Serialization Slide # 20

Customizing Deserialization


The readObject() method allows you to customize the deserialization of an object

The readObject() must have the signature given below

readObject must be implemented in the class of the object to be deserialized

   private void readObject( ObjectInputStream in)
      {
      // this is normally called first
      in.defaultReadObject();
      
      //Now you do the custom work
      }

defaultReadObject() throws the following exceptions

Throws: ClassNotFoundException
if the class of a serialized object could not be found.
Throws: IOException
if an I/O error occurs.
Throws: NotActiveException (subclass of IOException)
if the stream is not currently reading objects.


Doc 10, Serialization Slide # 21
Example of readObject

import java.io.Serializable;
import java.io.ObjectInputStream;
import java.io.IOException;
class Roger implements Serializable
   {
   private int lowBid;
   private transient float averageBid;
   private int highBid;
   
   public Roger(int lowBid, int highBid )
      {
      this.lowBid = lowBid;
      this.highBid = highBid;
      averageBid = (lowBid + highBid)/2;
      }
   
   private void readObject( ObjectInputStream in) throws 
         IOException, ClassNotFoundException
      {
      in.defaultReadObject();
      
      averageBid = (lowBid + highBid)/2;
      }
      
   public String toString()
      {
      return "Low: " + lowBid + "Ave: " + averageBid;
      }
   }

Doc 10, Serialization Slide # 22
// Example of readObject - Continued

class ReadObjectExample
   {
   public static void main( String args[] ) throws Exception
      {
      serialize();
      deserialize();
      }
      
   public static void serialize() throws IOException
      {
      OutputStream outputFile = 
         new FileOutputStream( "Serialized" );
      ObjectOutputStream cout = 
         new ObjectOutputStream( outputFile );
      cout.writeObject( new Roger( 1, 5 ));
      cout.close();
      }   
   public static void deserialize() throws Exception
      {
      InputStream inputFile = 
         new FileInputStream( "Serialized" );
      ObjectInputStream cin = 
         new ObjectInputStream( inputFile );
      System.out.println( cin.readObject() );
      }   
   }
Output
 Low: 1 Ave: 3.0

Doc 10, Serialization Slide # 23

Simple Customizing Serialization


The writeObject() method allows you to customize the serialization of an object

The writeObject() must have the signature given below

writeObject must be implemented in the class of the object to be deserialized

   private void writeObject( ObjectOutputStream out )          {
      // normally do the custom work before
      // you call defaultWriteObject()
      out.defaultWriteObject();
      }

defaultWriteObject() throws the following exceptions

Throws: IOException
if an I/O error occurs.
Throws: NotActiveException (subclass of IOException)
if the stream is not currently writing objects.

Doc 10, Serialization Slide # 24
Taking Complete Control
It may make more sense to use Externalizable if you want to take complete control. Externalizable is covered later. The difference is that Serializable creates name-value pairs for the fields in the output. In the example below we are storing the name-value pairs, but not using them. Then we append the values at the end of the byte stream. Using we do not store name-value pairs that are not being used.
class Roger implements Serializable {
   private int lowBid;
   private float averageBid;
   private int highBid;
   
   public Roger(int lowBid, int highBid ) {
      this.lowBid = lowBid;
      this.highBid = highBid;
      averageBid = (lowBid + highBid)/2;
   }
   
   private void readObject( ObjectInputStream in) 
      throws IOException {
      lowBid = in.readInt();
      averageBid = in.readFloat();
      highBid = in.readInt();
   }
   private void writeObject( ObjectOutputStream out) 
      throws IOException {
      out.writeInt( lowBid );
      out.writeFloat( averageBid );
      out.writeInt( highBid );
   }
   public String toString() {
      return " " + lowBid + " " + averageBid;
   }
}

Doc 10, Serialization Slide # 25
readObject/writeObject & Inheritance

Parent is Serializable

If the parent is Serializable, then the parents fields will be serialized/deserialized automatically.

Parent is not Serializable

The parents no-argument constructor will be called to initialize the object.

If you wish the parent’s fields to the reset to the proper values you need to do it in the child’s write/readObject method.

Example
public class Parent {
   String parentData = "In-line";
   public Parent() {
      parentData = "Constructor";
      System.out.println( "In parent");
   }
}

Doc 10, Serialization Slide # 26
Child Class
import java.io.Serializable;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.IOException;
import java.lang.ClassNotFoundException;
public class Roger extends Parent implements Serializable {
   private int lowBid;
   private int highBid;
   
   public Roger(int lowBid, int highBid ) {
      this.lowBid = lowBid;
      this.highBid = highBid;
      parentData = "Roger Constructor";
      System.out.println( "In Roger Constructor");
   }
   
   private void readObject( ObjectInputStream in) 
      throws IOException, ClassNotFoundException {
      in.defaultReadObject();
      parentData = in.readUTF();
   }
   private void writeObject( ObjectOutputStream out) 
      throws IOException, ClassNotFoundException {
      out.defaultWriteObject( );
      out.writeUTF( parentData );
   }
   public String toString() {
      return " parent:" + parentData;
   }
}


Doc 10, Serialization Slide # 27

Class Versions


The Problem

Start with a class:

class Unstable
   {
   double hereToday;
   }

Serialize an Unstable object to a file for later use

Now change the class

class Unstable
   {
   char goneTomorrow;
   }

After the class has changed, try to deserialize the object!

What should happen?
What could happen? (Hint nothing very good)

Doc 10, Serialization Slide # 28
Class serialVersionUID

Java generates a serialVersionUID for each class

When deserializing an object, the serialVersionUID of the deserialized object and it class are checked

If they do not agree, an exception is thrown

Information Used to Compute the serialVersionUID

1. Class name
2. The class modifiers
3. The name of each interface
4. For each field of the class(except private static and private transient fields):
The name of the field
The modifiers of the field
The descriptor of the field
5. For each method including constructors, except private methods and constructors:
The name of the method
The modifiers of the method
The descriptor of the method


Doc 10, Serialization Slide # 29
Controlling The Class Version

By declaring in your class a field of type and name
static final long serialVersionUID = XXXX;

you override the default method of computing the classes version number.

This is done when changes are made to a class that don't break deserialization of older objects.

For example, start with the class:

class Sam implements Serializable
   {
   private int lowBid;
   
   public Sam(int lowBid)
      {
      this.lowBid = lowBid;
      }
   }

Now add a toString() method to the class. This will change the class version. Objects serialized with the original Sam class will not be serializable with the Sam class containing the toString(). However, clearly we have not changed the class in any way to make it impossible to deserialize the old objects. If we added a serialVersionUID to the class and did not change its value, then deserialization would work with the modified class and old objects!

Doc 10, Serialization Slide # 30
Example of serialVersionUID

Start with this class:

class Roger implements Serializable
   {
   static final long serialVersionUID = 1L;
   private int lowBid;
   
   public Roger(int lowBid )
      {
      this.lowBid = lowBid;
      }
   }

Serialize it with the following program. The rest of the example will read this serialized version of the Roger object

class SerializeRoger
   {
   public static void main( String args[] ) throws Exception
      {
      OutputStream outputFile = 
         new FileOutputStream( "Serialized" );
      ObjectOutputStream cout = 
         new ObjectOutputStream( outputFile );
      cout.writeObject( new Roger( 1, 5 ));
      cout.close();
      }
   }

Doc 10, Serialization Slide # 31
Example of serialVersionUID - Continued

Change the class as follows and recompile

class Roger implements Serializable
   {
   static final long serialVersionUID = 1L;
   private int lowBid;
   
   public Roger(int lowBid )
      {
      this.lowBid = lowBid;
      }
   public String toString()
      {
      return " " + lowBid + " " + averageBid;
      }
   }

Now deserialize the original Roger object with the program. There is no problem as the class has not changed any data

class ReadRogerObejct
   {
   public static void main( String args[] ) throws Exception
      {
      InputStream inputFile = 
         new FileInputStream( "Serialized" );
      ObjectInputStream cin = 
         new ObjectInputStream( inputFile );
      System.out.println( cin.readObject() );
      }   
   }

Doc 10, Serialization Slide # 32
Example of serialVersionUID - Continued

Now we add some new fields to the class. Since they are not in the saved Roger object, we add the readObject method to make some reasonable values for the new fields. Compiling the class and deserialize in the original Roger object with the ReadRogerObejct class. It will work.

class Roger implements Serializable
   {
   static final long serialVersionUID = 1L;
   private int lowBid;
   private transient float averageBid;
   private int highBid;
   public Roger(int lowBid, int highBid )
      {
      this.lowBid = lowBid;
      this.highBid = highBid;
      averageBid = (lowBid + highBid)/2;
      }
   private void readObject( ObjectInputStream in) throws 
         IOException, ClassNotFoundException
      {
      in.defaultReadObject();
      if ( highBid == 0)
         highBid = lowBid;
      averageBid = (lowBid + highBid)/2;
      }
   public String toString()
      {
      return " " + lowBid + " " + averageBid;
      }
   }

Doc 10, Serialization Slide # 33
Example of serialVersionUID - Continued

We can change the class to the following and it will still work to deserialize the original Roger object. Note that the field "lowBid" is not in this class.

class Roger implements Serializable
   {
   static final long serialVersionUID = 1L;
   private int highBid;
   
   public Roger(int highBid)
      {
      this.highBid= highBid;
      }
   }

Doc 10, Serialization Slide # 34
Example of serialVersionUID - Continued

¿So what does not work?

This is actually three questions:

When will the deserialization throw an exception due to changes in the class?
What changes in the class logical require a new version?
What can be done to keep classes forward and backward compatible?

Changing the type of a field causes the deserialization to fail. The following class will cause to the deserialization of the original Roger object to fail.

class Roger implements Serializable
   {
   static final long serialVersionUID = 1L;
   private float lowBid;
   
   public Roger(int lowBid)
      {
      this.lowBid = lowBid;
      }
   }

The programmer will have to decide the answer to the second question on an individual basis. The third question is answered later. See the section on Version Control & JDK 1.2

Doc 10, Serialization Slide # 35

Serialver


When the class needs another version number use serialver to generate a new one. Sun recommends using the program serialver to generate the serialVersionUID for your class. This recommendation is given in the context of Java's RMI for distributing objects over a network. This recommendation is made for security reasons

Using serialver

serialver is located in the same directory as javac

1. Compile your class, make sure that the variable:
static final long serialVersionUID
is not defined in the class

2. run serialver using the command line:

serialvar  classname

3. The output of serialver is line of text to declare the variable serialVersionUID


Doc 10, Serialization Slide # 36
Example of Using serialver

import java.io.Serializable;
class Roger implements Serializable
   {
   public int lowBid;
   
   public Roger(int lowBid )
      {
      this.lowBid = lowBid;
      }
   
   public String toString()
      {
      return " " + lowBid;
      }
   }

Put the above code in the file Roger.java

eli-> javac Roger.java
eli-> serialver Roger
Roger:    static final long serialVersionUID = -8462350894591099987L;

Doc 10, Serialization Slide # 37

Version Control & JDK 1.2


The serialization process given so far does not provide enough flexibility to handle changing classes. JDK 1.2 adds some new features to serialization.

ObjectStreamField


There are two ways to declare fields to be serializable. The first is just to declare the class Serializable. The second is to use ObjectStreamField. To do this you must have a field in the class defined as:

private static final ObjectStreamField[] serialPersistentFields 
The final is optional, but all other aspects are required. ObjectOutputStream will automatically generate field data (type and name) for only those fields listed in the serialPersistentFields variable. The data is generated even if you do not write a value for the listed fields. No values of fields will be generated for you. You have to write the values out explicitly using the put() method of ObjectOutputStream.PutField. See example next slide. defaultWriteObject( ) of ObjectOutputStream does not work when using serialPersistentFields.

Doc 10, Serialization Slide # 38
Example
import java.util.ArrayList;
import java.io.Serializable;
import java.io.ObjectStreamField;
import java.io.ObjectInputStream;
import java.io.ObjectInputStream.GetField;
import java.io.ObjectOutputStream;
import java.io.ObjectInputStream.GetField;
import java.io.IOException;
import java.io.ObjectOutputStream;
public class Roger implements Serializable {
   private int lowBid;
   private ArrayList info = new ArrayList();
   
   private static final ObjectStreamField[] serialPersistentFields = { 
         new ObjectStreamField( "low", Integer.TYPE), 
         new ObjectStreamField( "high", ArrayList.class) 
   };
      
   public Roger(int lowBid, String highBidData ) {
      this.lowBid = lowBid;
      info.add( highBidData );
   }
   
   private void readObject( ObjectInputStream in) 
      throws IOException {
      try  {
         ObjectInputStream.GetField fields = in.readFields();
         lowBid = (int) fields.get("low", 0);
           info = (ArrayList) fields.get("high", new ArrayList() );
      } catch (Exception ClassNotFoundException)  {
         throw new IOException();
      }
   }
   private void writeObject( ObjectOutputStream out) 
      throws IOException {
      ObjectOutputStream.PutField fields = out.putFields();
      fields.put("low", lowBid);
      fields.put("high",  info);
      out.writeFields(  );
   }
}
Note: the fields.get() and fields.put() can be done in any order.

Doc 10, Serialization Slide # 39

Motivation

The following example is from the Sun documentation. The original can be found at: http://java.sun.com/products/jdk/1.2/docs/guide/serialization/examples/altimpl/index3.html The example starts with ARectangle class that stores its corners as four ints. The class evolves to store its corners as two Point objects. We want to serialize an object of either version of the class and deserialize that object using either version of the class. The second version of the class has the serialVersionUID of the first class, so to the serialize/deserialize mechanism the two classes are the same. Then the second class uses the serialPersistentFields mechanism to read/write the object’s state using the exact format of the first class.
Orginal Rectangle Class
/*
 * @(#)OriginalClass.java      1.4 98/10/01
 *
 * Copyright (c) 1997 Sun Microsystems, Inc. All Rights Reserved.
 */
import java.io.*;
import java.awt.*;
class ARectangle implements java.io.Serializable {
   int x1;
   int y1;
   int x2;
   int y2;
   ARectangle(int xone, int yone, int xtwo, int ytwo) {
      x1 = xone;
      y1 = yone;
      x2 = xtwo;
      y2 = ytwo;
   }
   public String toString() {
      return("x1: " + x1 + "\ny1: " + y1 + "\nx2: " + x2 + "\ny2: " + y2);
   }
}

Doc 10, Serialization Slide # 40
Revised Rectangle Class
/*
 * @(#)EvolvedClass.java        1.7 98/10/01        
 *
 * Copyright (c) 1997 Sun Microsystems, Inc. All Rights Reserved.
 */
import java.io.*;
import java.awt.*;
class ARectangle implements java.io.Serializable {
   Point point1;
   Point point2;
   static final long serialVersionUID = 9030593813711490592L;
   private static final ObjectStreamField[] serialPersistentFields = { 
      new ObjectStreamField("x1", Integer.TYPE), 
      new ObjectStreamField("y1", Integer.TYPE), 
      new ObjectStreamField("x2", Integer.TYPE), 
      new ObjectStreamField("y2", Integer.TYPE) 
      }; 
   
   ARectangle(int x1, int y1, int x2, int y2) {
      point1 = new Point(x1, y1);
      point2 = new Point(x2, y2);
   }
   private void writeObject(ObjectOutputStream s)
      throws IOException {
      
      ObjectOutputStream.PutField fields = s.putFields();
      fields.put("x1", point1.x);
      fields.put("y1", point1.y);
      fields.put("x2", point2.x);
      fields.put("y2", point2.y);
      
      s.writeFields();
   }
   

Doc 10, Serialization Slide # 41
   private void readObject(ObjectInputStream s)
      throws IOException {
      ObjectInputStream.GetField fields = null;
      try { 
         fields = s.readFields();
      } catch (Exception ClassNotFoundException) {
         throw new IOException();
      }
      int x1 = (int)fields.get("x1", 0);
      int y1 = (int)fields.get("y1", 0);
      int x2 = (int)fields.get("x2", 0);
      int y2 = (int)fields.get("y2", 0);
      point1 = new Point(x1, y1);
      point2 = new Point(x2, y2);
   }
   public String toString() {
      return("point1.x: " + point1.x + "\npoint1.y: " + point1.y + "\npoint2.x: " + point2.x + "\npoint2.y: " + point2.y);
   }
}

Doc 10, Serialization Slide # 42

Externalizable


The java.io.Externalizable interface allows you to take complete control of the serialization process. You need to do the following:


public void readExternal(ObjectInput in) 
   throws IOException, ClassNotFoundException
public void writeExternal(ObjectOutput out) 
   throws IOException


Order is important here. When using Serializable, each field is stored with a label, so you can read values out of order. With Externalizable no labels are stored. You are writing bytes, with markers between entries.


This can be done by calling methods in the parent class. See http://java.sun.com/products/jdk/1.2/docs/guide/serialization/examples/index.html for examples of dealing with parent classes.

Doc 10, Serialization Slide # 43
Externalizable Example
import java.io.Externalizable;
import java.io.ObjectOutput;
import java.io.ObjectInput;
import java.io.IOException;
public class Roger implements Externalizable {
   private int lowBid;
   private String highBid;
   
   public Roger() {
      lowBid = -1;
      highBid = "none";
   }
   public Roger(int lowBid, String highBidData ) {
      this.lowBid = lowBid;
      this.highBid = highBidData;
   }
   
   public void readExternal(ObjectInput in) 
      throws IOException, ClassNotFoundException {
      lowBid = in.readInt();
      highBid = (String) in.readObject();
   }
   public void writeExternal(ObjectOutput out) 
      throws IOException {
      out.writeInt( lowBid );
      out.writeObject( highBid );
   }
   public String toString() {
      return " " + lowBid + "high: " + highBid;
   }
}

Doc 10, Serialization Slide # 44

Javadoc & Serialization


There are three tags used to document Serialization/Externalizable. In JDK 1.2 if you do not use these tags, javadoc will complain.

@serial
The @serial tag should be placed in the javadoc comment for a default serializable field. The syntax is as follows: @serial field-description. The optional field-description describes the meaning of the field and its acceptable values. The field-description can span multiple lines. When a field is added after the initial release, a @since tag indicates the version the field was added. The field-description for @serial provides serialization-specific documentation and is appended to the javadoc comment for the field within the serialized form documentation.

@serialField
The @serialField tag is used to document an ObjectStreamField component of a serialPersistentFields array. One of these tags should be used for each ObjectStreamField component. The syntax is as follows: @serialField field-name field-type field-description.

@serialData
The @serialData tag describes the sequences and types of data written or read. The tag describes the sequence and type of optional data written by writeObject or all data written by the Externalizable.writeExternal method. The syntax is as follows: @serialData data-description

For example use of serialField and serialData see ARectangle class in http://java.sun.com/products/jdk/1.2/docs/guide/serialization/examples/altimpl/EvolvedClass.java

Doc 10, Serialization Slide # 45

Protocol Changes JDK 1.1 to JDK 1.2


The JDK 1.2 uses an improved protocol to serialize objects. You can use ObjectOutputStream.useProtocolVersion to tell JDK 1.2 to write with the JDK 1.1 protocol. For more details see section 6.3 Stream Protocol Versions of Java Object Serialization Specification at: http://java.sun.com/products/jdk/1.2/docs/guide/serialization/spec/protocol.doc3.html#5849

Copyright ©, All rights reserved.
1999 SDSU & Roger Whitney, 5500 Campanile Drive, San Diego, CA 92182-7700 USA.
OpenContent license defines the copyright on this document.

Previous    visitors since 23-Feb-99    Next