[ Team LiB ] Previous Section Next Section

10.3 Externalizable Classes

The Externalizable interface extends Serializable and defines the writeExternal( ) and readExternal( ) methods. An Externalizable object may be serialized as other Serializable objects are, but the serialization mechanism calls writeExternal( ) and readExternal( ) to perform the serialization and deserialization. Unlike the readObject( ) and writeObject( ) methods in Example 10-2, the readExternal( ) and writeExternal( ) methods can't call the defaultReadObject( ) and defaultWriteObject( ) methods: they must read and write the complete state of the object by themselves.

It is useful to declare an object Externalizable when the object already has an existing file format or when you want to accomplish something that is simply not possible with the standard serialization methods. Example 10-3 defines the CompactIntList class, an Externalizable subclass of the IntList class of Example 2-7. CompactIntList makes the assumption that it is typically used to store small integers that fit in two bytes instead of four; it implements Externalizable so it can define a serialized form that is more compact than the format used by ObjectOutputStream and ObjectInputStream.

Example 10-3. CompactIntList.java
package je3.serialization;
import je3.classes.IntList;
import java.io.*;

/**
 * This subclass of IntList assumes that most of the integers it contains are
 * less than 32,000.  It implements Externalizable so that it can define a 
 * compact serialization format that takes advantage of this fact.
 **/
public class CompactIntList extends IntList implements Externalizable {
    /**
     * This version number is here in case a later revision of this class wants
     * to modify the externalization format, but still retain compatibility
     * with externalized objects written by this version
     **/
    static final byte version = 1;

    /**
     * This method from the Externalizable interface is responsible for saving
     * the complete state of the object to the specified stream.  It can write
     * anything it wants as long as readExternal( ) can read it.
     **/
    public void writeExternal(ObjectOutput out) throws IOException {
        trim( );  // Compact the list to its current size

        out.writeByte(version);  // Start with our version number.
        out.writeInt(size);      // Output the number of array elements
        for(int i = 0; i < size; i++) {  // Now loop through the array
            int n = data[i];             // The array element to write
            if ((n <= Short.MAX_VALUE) && (n > Short.MIN_VALUE)) {
                // If n fits in a short and is not Short.MIN_VALUE, then write
                // it out as a short, saving ourselves two bytes
                out.writeShort(n);
            }
            else {
                // Otherwise write out the special value Short.MIN_VALUE to
                // signal that the number does not fit in a short, and then
                // output the number using a full 4 bytes, for 6 bytes total
                out.writeShort(Short.MIN_VALUE);
                out.writeInt(n);
            }
        }
    }

    /**
     * This Externalizable method is responsible for completely restoring the
     * state of the object.  A no-arg constructor will be called to recreate
     * the object, and this method must read the state written by 
     * writeExternal( ) to restore the object's state.
     **/
    public void readExternal(ObjectInput in)
        throws IOException, ClassNotFoundException
    {
        // Start by reading and verifying the version number.
        byte v = in.readByte( );
        if (v != version)
            throw new IOException("CompactIntList: unknown version number");

        // Read the number of array elements, and make array that big
        int newsize = in.readInt( );
        setCapacity(newsize);  // A protected method inherited from IntList
        this.size = newsize;   // Save this size.

        // Now read that many values from the stream
        for(int i = 0; i < newsize; i++) {
            short n = in.readShort( );
            if (n != Short.MIN_VALUE) data[i] = n;
            else data[i] = in.readInt( );
        }
    }

    /** A main( ) method to prove that it works */
    public static void main(String[  ] args) throws Exception {
        CompactIntList list = new CompactIntList( );
        for(int i = 0; i < 100; i++) list.add((int)(Math.random( )*40000));
        CompactIntList copy = (CompactIntList)Serializer.deepclone(list);
        if (list.equals(copy)) System.out.println("equal copies");
        Serializer.store(list, new File("compactintlist.ser"));
    }
}
    [ Team LiB ] Previous Section Next Section