Understanding Java Serialization
by Govind Seshadri
Data persistency - A basic need
Any halfway-useful application needs data persistency.
If you've been wondering if there is a simple way to keep objects
persistent in Java, you've certainly chosen the right time to have started to
think about the issue. The JDK 1.1 provides us with the Object Serialization
mechanism to tackle the once notorious problem of object persistency. Using
Serialization, you can seamlessly back up data objects to a file, or send
them to distributed Java servers via Remote Method Invocation.
As its name probably implies, Serialization converts Java objects into a byte
stream, which can then be easily stored to a file. When the file is later
read, the byte stream can be easily converted to the original objects.
But what about JDBC?
Some of you may be asking, "Don't I already have JDBC to keep my data
persistent?" While it's true that storing data within a full-fledged database
would obviously take care of the persistency issue, it would be like using a
sledgehammer to pound a nail. JDBC is a fairly complex interface that allows
you to integrate high-powered relational databases into your Java
applications. If your application is a fairly small departmental application,
with a storage requirement on the order of a few hundred records, using an
Oracle or Sybase database as the repository is pure overkill. The problem can
be solved more easily with a flat-file storage mechanism utilizing
Serialization.
Even Relational databases may not be your answer if your storage needs are
extremely large, and if you have a large end-user community requiring a
high-throughput system. Keep in mind that every time you need to
store/retrieve data from a Relational database, you need to map data stored
within two-dimensional tables to Java objects. This mapping would not only
sap programmer productivity, but also affect overall system performance. An
ideal solution for large, mission-critical applications would be to use
Object databases. Even though there is a fairly steep learning-curve in
ramping up to this new paradigm, it may be well worth exploring in the
future.
Under the hood
Let's try and understand the workings of object Serialization through the
following example:
Listing 1 Person.java
public class Person implements java.io.Serializable {
int id;
String name;
String addr;
public Person(int key, String n, String a) {
id = key;
name=n;
addr=a;
}
}
There is nothing unusual about the Person class in Listing 1, except that it
implements the Serializable interface. All objects that are converted to a
byte stream during Serialization need to implement the Serializable
interface.
Listing 2 Rolodex.java
class Rolodex implements java.io.Serializable {
Person[] data =new Person[5];
public Rolodex() {
data[0] = new Person(100,"Lazy Larry","123 Easy Drive");
data[1] = new Person(101,"Happy Harry","30 Sunset Ave");
data[2] = new Person(102,"Big Bessie","11 Market Way");
data[3] = new Person(103,"Guitar Man","461 Ocean Blvd");
data[4] = new Person(104,"Java Joe","1 Hacker Ave");
}
public void display() {
for (int i=0; i<5;i++) {
System.out.println("ID: "+data[i].id);
System.out.println("NAME: "+data[i].name);
System.out.println("ADDR: "+data[i].addr);
}
}
}
Listing 2 shows the Rolodex class, which also implements the
Serializable interface. This class is essentially an array of Person
objects, initialized with some values. We will see how we can seamlessly read
and write an object of this class to a file using Serialization.
Listing 3 RoloWrite.java
import java.io.FileOutputStream;
import java.io.ObjectOutputStream;
public class RoloWrite {
public static void main(String[] args) {
Rolodex rolodex = new Rolodex();
try {
FileOutputStream fo =
new FileOutputStream("rolodata");
ObjectOutputStream serial =
new ObjectOutputStream(fo);
serial.writeObject(rolodex);
serial.flush();
}
catch (Exception e) {
System.out.println("Error during object
serialization");
}
}
}
Listing 3 shows the RoloWrite class, which demonstrates how easily a Java
object can written to a file, after first converting it to a byte stream. We
first create an object of the Rolodex class, which we intend to keep
persistent. The FileOutputStream object is created because we need some
stream object to attach our ObjectOutputStream to. The ObjectOutputStream
object created by attaching the stream now contains all the necessary
functionality to serialize any Java object and write it to the stream.
To write our Rolodex object out to the stream, all we have to do is invoke
the writeObject method of the ObjectOutputStream as in
serial.writeObject(rolodex);
Notice that we did not have to make any other conversion to the object itself
in trying to make it persistent. We simply just wrote the object to the
stream, as is!
Listing 4 RoloRead.java
import java.io.FileInputStream;
import java.io.ObjectInputStream;
public class RoloRead {
public static void main(String[] args) {
Rolodex rolodex=null;
try {
FileInputStream fi =
new FileInputStream("rolodata");
ObjectInputStream serial =
new ObjectInputStream(fi);
rolodex =(Rolodex) serial.readObject();
}
catch (Exception e) {
System.out.println("Error during object
serialization");
}
rolodex.display();
}
}
The RoloRead class shown in Listing 4, first opens a stream to the data file
using FileInputStream. Once we attach this stream to the
ObjectInputStream object, we are ready to read the object, via the
readObject method. Notice that the object returned by readObject needs to be
cast to your target class - Rolodex, as Java does not
automatically do this for you. On calling the display method on the
Rolodex retrieved object, it's contents are displayed.
RoloWrite.java and RoloRead.java can be compiled as usual. When you run
RoloRead after creating an image of the object by first running RoloWrite,
you get the following output:
ID: 100
NAME: Lazy Larry
ADDR: 123 Easy Drive
ID: 101
NAME: Happy Harry
ADDR: 30 Sunset Ave
ID: 102
NAME: Big Bessie
ADDR: 11 Markey Way
ID: 103
NAME: Guitar Man
ADDR: 461 Ocean Blvd
ID: 104
NAME: Java Joe
ADDR: 1 Hacker Ave
One size doesn't fit all
Although Serialization provides a neat and simple interface for object
persistency, it is best suited for applications that use only a small amount
of data, where concurrent access to data is not required. Since Serialization
reads/writes entire objects after converting them into a byte stream, system
throughput suffers if your objects are greater than, say, a megabyte. System
performance also lags if your application is either read- or write-
intensive, for the same reason. It should be noted that Serialization does
not provide any of the more advanced database features like file locking,
record locking, commits, rollbacks, etc. Hence, they may not be suited for
applications where reliability is paramount.
Author footnote...
Govind Seshadri leads Java development at MGIC Investment Corporation. His
primary areas of interest include design of large-scale Java Client/Server systems,
Network Security and Electronic Commerce. Govind holds Bachelors and Masters
degrees in Computer Science. He can be reached at
Govind_Seshadri@mgic.com