Preventing Serialization
Home ] Up ] [ Preventing Serialization ] Custom Serialization ]

 

 

If you wish an entire class not to be serializable, you can simply not have that class implement Serializable.

If you wish a specific field in a class not to be serialized, you can declare it as transient.

When you serialize out a class object, the writeObject() method will write out information about the class of the object, the signature of the class, and the values of any field that is not transient.

Consider the following example. Here's a modified version of the Person class I used earlier (I've added setName() and setAge() methods). I've also introduced a new class, RecordedPerson.

package serialization;

import java.io.Serializable;

public class Person
    implements Serializable
{
    public Person(String name, int age)
    {
        m_name = name;
        m_age  = age;
    }
    
    public String getName()
    {
        return m_name;
    }
    
    public void setName(String name)
    {
        m_name = name;
    }
    
    public int getAge()
    {
        return m_age;
    }
    
    public void setAge(int age)
    {
        m_age = age;
    }
    
    public String toString()
    {
        String s = getClass().getName();
        // Strip off package name
        int index = s.lastIndexOf('.');
        if (index != -1)
            s = s.substring(index+1, s.length());
            
        s += ":Hashcode=@" + Integer.toHexString(hashCode()) + 
             ", Name=" + m_name + ", Age=" + m_age;
        return s;
    }
    
    /////// Data ////////
    private String  m_name;
    private int     m_age;
}
package serialization;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintWriter;

public class RecordedPerson 
    extends Person
{
    // Constructor to create a recorded person and 
    // the associated record file
    public RecordedPerson(String name, int age, int id)
    {
        super(name, age);
        m_id = id;
        createRecordFile();
    }
    
    // Constructor to create a recorded person
    // and to open an already existing record file.
    public RecordedPerson(int id)
    {
        super(null, -1);
        m_id = id;
        setRecordFile();
        setBasicData();
    }
    
    // Reads the record from the record file
    // and returns it as a string.
    public String getRecord()
    {
        String data = "";
        BufferedReader in = null;
        try
        {
            in = new BufferedReader(
                         new FileReader(m_recordFile));
            while(true)
            {
                String line = in.readLine();
                if (line == null)
                    break;
                data += line + "\n";
            }
        }
        catch(IOException e)
        {
            e.printStackTrace();
        }
        finally
        {
            if (in != null)
            {
                try
                {
                    in.close();
                }
                catch(IOException e)
                {}
            }
        }
        return data;
    }
    
    // Adds a new item to the record file for this person
    public void addRecord(String item)
    {
        PrintWriter out = null;
        try
        {
            out = new PrintWriter(
                        new FileWriter(
                                m_recordFile.getAbsolutePath(),
                                true));
            out.println(item);
        }
        catch(IOException e)
        {
            e.printStackTrace();
        }
        finally
        {
            if (out != null)
                out.close();
        }
    }
    
    // Creates a new record file with name and age record
    // (If the record file already exists, assumes that it's valid)
    private void createRecordFile()
    {
        setRecordFile();
        if (!m_recordFile.exists())
        {
            // Create a new record file
            PrintWriter out = null;
            try
            {
                out = new PrintWriter(
                                new FileWriter(m_recordFile));
                out.println(getName() + "," + getAge());
            }
            catch(IOException e)
            {
                e.printStackTrace();
            }
            finally
            {
                if (out != null)
                    out.close();
            }
        }
        else
        {
            // Assume for simplicity that the name and age
            // actually correspond with the current ones.
        }
    }
    
    // Sets the record File object
    // If necessary, creates the subdirectory for record files.
    private void setRecordFile()
    {
        File records = new File("records");
        if (!records.exists())
            records.mkdir();    // Create directory
        m_recordFile = new File(records, "r" + m_id + ".rec");
    }
    
    // Reads the record file and extracts the name and age
    // and sets those values for the current recorded person.
    private void setBasicData()
    {
        // Extract the name and age from the record
        String record = getRecord();
        int commaIndex = record.indexOf(',');
        int eolIndex = record.indexOf('\n');
        String name = record.substring(0, commaIndex);
        String age = record.substring(commaIndex+1, eolIndex);
        // Set the name and age values
        setName(name);
        setAge(Integer.parseInt(age));
    }
    
    // Creates a string containing RecordedPerson information.
    public String toString()
    {
        String s = super.toString();
        s += ", ID=" + m_id + ", recordFile=" + m_recordFile;
        return s;
    }
    //// Data ////
    private int   m_id;
    private File  m_recordFile;
}

Here's a program that serializes out a RecordedPerson object in the normal way:

package serialization;

import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectOutputStream;

public class RecordedSerializeOut
{
    public static void main(String[] args)
    {
        RecordedPerson rc = new RecordedPerson(
                                    "Franco Limpone", 42, 666);
        System.out.println("rc: " + rc);                                    
        System.out.println(rc.getRecord());

        ObjectOutputStream out = null;
        try
        {
            out = new ObjectOutputStream(
                          new FileOutputStream(
                                   "recorded.dat"));
            out.writeObject(rc);
            System.out.println("Serialization successful.");
        }
        catch(IOException e)
        {
            e.printStackTrace();
            System.out.println("Serialization failed.");
        }
        finally
        {
            if (out != null)
            {
                try
                {
                    out.close();
                }
                catch(IOException e)
                {}
            }
        }
    }
}

It outputs the following on my machine:

Notice that the record file is records\r666.rec, which is not a portable file specification.

If we serialize this back in using the following program:

package serialization;

import java.io.FileInputStream;
import java.io.IOException;
import java.io.ObjectInputStream;

public class RecordedSerializeIn
{
    public static void main(String[] args)
    {
        ObjectInputStream in = null;
        boolean failed = true;
        try
        {
            in = new ObjectInputStream(
                          new FileInputStream(
                                   "recorded.dat"));
            RecordedPerson rc = (RecordedPerson)in.readObject();
            System.out.println("rc: " + rc);
            System.out.println(rc.getRecord());
            System.out.println("Serialization successful.");
            failed = false;
        }
        catch(IOException e)
        {
            e.printStackTrace();
        }
        catch(ClassNotFoundException e)
        {
            e.printStackTrace();
        }
        finally
        {
            if (in != null)
            {
                try
                {
                    in.close();
                }
                catch(IOException e)
                {}
            }
            if (failed)
                System.err.println("Serialization failed.");
        }
    }
}

This program outputs the following on my machine (which is the same machine on which I serialized it out):

The recordFile is set to records\r666.rec, which means that the File object was successfully serialized out and then back in. However, ask yourself the following:

  • What would have happened if the serialization back in had occurred on another machine?
  • What would have happened if the program which did the serialization back in had a different default user directory from the program which did the serialization out?

Now, it turns out that the File class code ensures during serialization in that the separator ('\') is correctly set, so presumably the code would work on another machine (although I wouldn't necessarily bet on it!).

So let's say we don't want to take the risk, and so we choose not to serialize the recordFile field. We can do this by merely changing one line in RecordedPerson:

private transient File  m_recordFile;

which makes no perceptable difference in the output of the program that serializes out. However, if we run the program that serializes back in, it outputs the following:

So, clearly, the recordFile field contents did not get serialized out, and its value was set to null on serialization back in.

What to do? We have to do some custom serialization...

 

The page was last updated February 19, 2008