Object Serialization

Normally, objects do not persist beyond the lifetime of the program that created them. However, persistence is required when we write objects to a file to be later read back in, possibly by a different program. Although we have seen how to read and write primitive data types and text to a file, we require a mechanism for storing the state of an object. Serialization converts an object and its state to a stream of bytes. RMI (Remote Method Invocation) also uses serialization to transparently communicate with objects on remote machines.

The JVM automatically handles most details of serialization, at least for default cases. However, Java provides a mechanism where we can customize the serialization process if required.

Serialization is a large topic, and we cover just the basics here. Topics such as class versioning, or evolution, have been omitted.

The byte streams ObjectInputStream and ObjectOutputStream allow us to read and write objects. As an example, consider the following Account class. Account includes a constructor that throws a customized ValueTooSmallException if we attempt to create an Account object with a negative balance.


ValueTooSmallException

public class ValueTooSmallException extends
Exception
{
public ValueTooSmallException(){}

public ValueTooSmallException(String message)
{
super(message);
}
}

Account

import java.io.*;

class Account implements Serializable
{
int accountNo;
string accountName;
double balance;
Account(int accountNo,String accountName, double balance
)
throws ValueTooSmallException
{
this.accountNo = accountNo;

this.accountNo = accountName;
if (balance < 0 )
{

throw new ValueTooSmallException(''Negative Balance
");
Else
{
this.balance = balance;
}
}
}


Suppose we want to write Account objects to a file and subsequently read these from the file. The first requirement for writing an object to a stream is that the corresponding class must implement the Serializable interface. So note the implements Serializable clause in line 3. Note that the Serializable interface does not require us to implement any methods. Such a no-method interface is known as a marker interface.

A second requirement is that the object's member variables are nonstatic. Serialization will not affect static variables; they may be different when the object is read back in, or deserialized.

WriteAccount is a program that writes two Account objects, account1 and account2,
to a file acc.dat.

WriteAccount

import java.io.*;

class WriteAccount
{

public static void main(String[] args) throws
IOException, ValueTooSmallException
{
Account account1 = new Account(1, "account1", 40);
Account account2 = new Account(2, "account2", 100);
FileOutputStream out = new FileOutputStream("acc.dat");
ObjectOutputStream outob = new ObjectOutputStream(out);
outob.writeObject(account1);
outob.writeObject(account2);
outob.close();
out.close();
}
}


In line 9, we create a FileOutputStream object, out, that outputs to file acc.dat. Line 10 creates an ObjectOutputStream object, outob, wrapped around out. It is important to note that an ObjectOutputStream object can be wrapped around any byte OutputStream, not just a FileOutputStream. In lines 11–12, we use the ObjectOutputStream writeObject method to write the account1 and account2 objects to outob.

ReadAccount is a program that reads the Account objects from the acc.dat file using
an ObjectInputStream.

ReadAccount

import java.io.*;

class ReadAccount
{

public static void main(String[] args) throws
IOException, ClassNotFoundException
{
FileInputStream in = new FileInputStream(''acc.dat");
ObjectInputStream inobj = new ObjectInputStream(in);
Account acc1 = (Account) inobj.readObject();
Account acc2 = (Account) inobj.readObject();
System.out.println(" 1st number : " + acc1.accountNo);
System.out.println(" 2nd balance : " + acc2.balance);
inobj.close();
in.close();
}
}


In lines 7–8, we create an ObjectInputStream object, inobj, wrapped around a FileInputStream object, in, which is connected to the acc.dat file. In lines 9–10, we use the ObjectInputStream readObject method to read the Account objects from the ObjectInputStream. readObject returns an Object type, so this needs to be cast to Account. readObject reads the objects in the same order as they were written
to the acc.dat file. So acc1 corresponds to the account1 object in the WriteAccount program. readObject throws a ClassNotFoundException, so this exception should be present in the throws clause of the declaration (lines 5–6), together with the IOException thrown by all the stream methods.

The objects being written and read may be considerably more complex than this. For example, the Account class may have a reference to a Branch object. Furthermore, a number of Account objects may refer to the same Branch. To avoid making multiple copies of the Branch object, Java gives each object a serial number when writing to an ObjectOutputStream, hence the name serialization for this process. The process of reading back the object from the ObjectInputStream is called deserialization.

Note that when serializing an object, any referenced object must also be serializable. In fact, all objects in the referenced graph, known as transitive closure, must be serializable.

No comments:

Post a Comment