Serializing Families of Classes
Home ] Up ] Object Streams and Serialization ] [ Serializing Families of Classes ] Serialization and Class Versioning ] Custom Serialization and Security ] Serialized Applets ] Advanced Serialization ]

 

 

A number of issues arise when you attempt to serialize a related set of class instances:

  • All of the classes must be marked as Serializable
  • If the same object is referenced from more than one place, that object should still be serialized only once
  • There should be no situation where unlimited recursion occurs

The really nice thing about serialization is that the second and third items are taken care of automatically.

Let's take our previous example of simple serialization, and modify the classes somewhat, to emphasize what happens when there are multiple references to the same object:

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 int getAge()
    {
        return m_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;

public class Employee
    extends Person
{
    public Employee(String name, int  age, 
                    double salary)
    {
        super(name, age);
        m_salary = salary;
    }
    
    public double getSalary()
    {
        return m_salary;
    }
    
    public void setManager(Manager mgr)
    {
        m_manager = mgr;
    }
    
    public Manager getManager()
    {
        return m_manager;
    }
    
    public String toString()
    {
        String s = super.toString();
        s += ", Salary=" + m_salary;
        return s;
    }
    
    /////// Data ///////
    private double  m_salary;
    private Manager m_manager;
}
package serialization;

import java.util.Enumeration;
import java.util.Vector;

public class Manager
    extends Employee
{
    public Manager(String name, int  age,
                   double salary)
    {
        super(name, age, salary);
    }
    
    public void setSecretary(Employee sec)
    {
        m_secretary = sec;
    }
    
    public Employee getSecretary()
    {
        return m_secretary;
    }
    
    public void addDirectReport(Employee emp)
    {
        m_directReports.addElement(emp);
    }

    public void removeDirectReport(Employee emp)
    {
        m_directReports.removeElement(emp);
    }
    
    public int getDirectReportsCount()
    {
        return m_directReports.size();
    }
    
    public Enumeration getDirectReports()
    {
        return m_directReports.elements();
    }
    
    public String toString()
    {
        String s = super.toString();
        s += ", Secretary=[" + m_secretary + "]";
        return s;
    }
    
    ///// Data ////
    private Vector   m_directReports = new Vector();
    private Employee m_secretary;
}
package serialization;

import java.io.Serializable;
import java.util.Enumeration;
import java.util.Vector;

public class Organization
    implements Serializable
{
    public Organization(String name)
    {
        m_name = name;
    }
    
    public String getName()
    {
        return m_name;
    }
    
    public void setPresident(Manager pres)
    {
        m_president = pres;
    }
    
    public Manager getPresident()
    {
        return m_president;
    }
    
    public void addEmployee(Employee emp)
    {
        m_employees.addElement(emp);
    }

    public void removeEmployee(Employee emp)
    {
        m_employees.removeElement(emp);
    }
    
    public int getEmployeeCount()
    {
        return m_employees.size();
    }
    
    public Enumeration getEmployees()
    {
        return m_employees.elements();
    }
    
    public String toString()
    {
        String s = getName() + " has " +
                   getEmployeeCount() + " employees:\n";
        //Create organization chart, starting at president
        s += toString(m_president, 0);
        return s;
    }
    
    private static String toString(Employee emp, 
                                   int indentLevel)
    {
        String indent = "";
        for (int i = 0; i < indentLevel; i++)
            indent += "  ";
        String s = indent;
        s += emp.toString() + "\n";
        if (emp instanceof Manager)
        {
            Manager mgr = (Manager)emp;
            Enumeration en = mgr.getDirectReports();
            while (en.hasMoreElements())
            {
                Employee e = (Employee)en.nextElement();
                s += toString(e, indentLevel + 1);
            }
        }
        return s;
    }
    
    //// Data ////
    private String  m_name;
    private Manager m_president;
    private Vector  m_employees = new Vector();
}

I modified the classes to add a reference for each Employee's Manager, and each Manager's secretary. I augmented the Person class's toString() method to show more specifics about the object (including its hashcode), and I removed the m_id field from Employee to simplify the output. Finally, I added a new class, Organization, which has references to each of its Employees, and contains a toString() method which is capable of returning an "org chart".

Here's the modified program to serialize out the family of related objects:

package serialization;

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

public class FamilySerializeOut
{
    public static void main(String[] args)
    {
        Organization org = createOrganization();
        System.out.println("Org Chart:\n" + org);
        
        // Make asarray of all the organization's employees...
        Employee[] emps = new Employee[org.getEmployeeCount()];
        System.out.println("emps:");
        Enumeration en = org.getEmployees();
        for (int emp = 0; en.hasMoreElements(); emp++)
        {
            emps[emp] = (Employee)en.nextElement();
            System.out.println("[" + emp + "]:" + emps[emp]);
        }
        
        ObjectOutputStream out = null;
        boolean failed = true;
        try
        {
            out = new ObjectOutputStream(
                          new FileOutputStream(
                                   "org.dat"));
            // Serialize out the organization
            out.writeObject(org);
            // Serialize out the array of employees
            out.writeObject(emps);
            
            failed = false;
            System.out.println("Serialization successful.");
        }
        catch(IOException e)
        {
            e.printStackTrace();
        }
        finally
        {
            if (out != null)
            {
                try
                {
                    out.close();
                }
                catch(IOException e)
                {}
            }
            if (failed)
                System.out.println("Serialization failed.");
        }
    }
    
    private static Organization createOrganization()
    {
        Organization org = new Organization("Microsquish");
        Manager billg = createManager(org, null, null,
                                      "Bill Gates", 47,
                                      100000000000.00);
        org.setPresident(billg);
        
        Employee bettyb = createEmployee(org, billg,
                                         "Betty Boop", 56,
                                         36000.00);
        billg.setSecretary(bettyb);
        
        createSalesOrg(org);
        createEngineeringOrg(org, bettyb);
        
        return org;
    }
    
    private static Manager createSalesOrg(Organization org)
    {
        Manager elvisp = createManager(
                                    org, 
                                    org.getPresident(), 
                                    null,
                                    "Elvis Presley", 57,
                                    2000000.00);
        Employee oliveo = createEmployee(
                                    org, elvisp,
                                    "Olive Oyl", 48,
                                    32000.00);
        elvisp.setSecretary(oliveo);
        
        createEmployee(org, elvisp, 
                       "Prince", 25, 200000.00);
        createEmployee(org, elvisp,
                       "Madonna", 36, 400000.00);
        
        return elvisp;
    }
    
    private static Manager createEngineeringOrg(
                                    Organization org,
                                    Employee sec)
    {
        Manager carlp = createManager(
                                    org, 
                                    org.getPresident(), 
                                    sec,
                                    "Carl Perkins", 59,
                                    2000000.00);       
        createEmployee(org, carlp, 
                       "Porky Pig", 42, 20000.00);
        createEmployee(org, carlp,
                       "Taz Devil", 40, 40000.00);
        
        return carlp;
    }
    
    private static Manager createManager(
                                    Organization org, 
                                    Manager boss,
                                    Employee sec,
                                    String name, int age,
                                    double salary)
    {
        Manager mgr = new Manager(name, age, salary);
        doHousekeeping(mgr, org, boss);
        if (sec != null)
            mgr.setSecretary(sec);
        
        return mgr;
    }
    
    private static Employee createEmployee(
                                    Organization org,
                                    Manager boss,
                                    String name, int age,
                                    double salary)
    {
        Employee emp = new Employee(name, age, salary);
        doHousekeeping(emp, org, boss);
        return emp;
    }
    
    private static void doHousekeeping(
                                Employee emp,
                                Organization org, 
                                Manager boss)
    {
        org.addEmployee(emp);
        if (boss != null)
        {
            boss.addDirectReport(emp);
            emp.setManager(boss);
        }
    }
}

which adds a number of methods to create an Organization, and populate it with Employees and Managers and secretaries, and uses the Organization's toString() method to print out the entire "org chart". Then, to show that the handling of multiple references works across calls to writeObject(), it creates an array of Employees and populates it with references to all the organization's employees. In the process, it prints out the contents of the array. Finally, it serializes out the organization and then the array of employees.

Here's what it outputs on my machine (note the hashcode values -- each one identifies a unique object):

Now, let's serialize the results back in from the file:

package serialization;

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

public class FamilySerializeIn
{
    public static void main(String[] args)
    {
        ObjectInputStream in = null;
        boolean failed = true;
        try
        {
            in = new ObjectInputStream(
                          new FileInputStream(
                                   "org.dat"));
            Organization org = (Organization)in.readObject();
            System.out.println("Org Chart:\n" + org);
            Employee[] emps = (Employee[])in.readObject();
            System.out.println("emps:");
            for (int emp = 0; emp < emps.length; emp++)
                System.out.println("[" + emp + "]:" + emps[emp]);
            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.");
        }
    }
}

Note how little had to be changed!

This outputs, on my machine (again, note the hashcode values):

This shows that each unique object was serialized out once, and then serialized back in only once.

 
The page was last updated February 19, 2008