Home > DeveloperSection > Articles > Java I/O: Versioning Objects

Java I/O: Versioning Objects


Java Java  Java I/O 
Ratings:
0 Comment(s)
 132  View(s)
Rate this:

Java I/O: Versioning Objects

We have covered how to persist the object state to a disk file. In many situations, the classes in a program evolve over time. When a class definition changes, the object’s data saved with previous versions of the class becomes mostly unreadable. Versioning objects can manage this kind of situation where we are trying to read from an older version of the class. We will demonstrate this problem with a typical example in a practical situation.

Program Code               

Product Class:  Program to serialize the product class

Consider the program shown  here:

import java.io.*;

 

public class ProductWriter {

                   public static void main(String args[]) throws IOException {

                     Product p1 = new Product(100);

                     ObjectOutputStream os = new ObjectOutputStream(new FileOutputStream(

                                                "product.dat"));

                     os.writeObject(p1);

                     os.close();

                   }

}

 

class Product implements Serializable {

                     private float price;

                     private float tax;

 

                     public Product(float price) {

                             this.price = price;

                             tax = (float) (price * 0.20);

                     }

}

 

·         The program declares a Product class that has two fields, price and tax, of type float.

·         The main function constructs an instance of Product and saves it to a physical disk file.

·         After the data is saved at a later time, we want to read this data from the disk file and use it in future applications. For this, we write a ProductReader class, as shown here

Product Reader Class: Program to Read Serialized Product Data

import java.io.*;

 

public class ProductReader {

                     public static void main(String args[]) throws Exception {

                             ObjectInputStream is = new ObjectInputStream(new FileInputStream(

                                                "product.dat"));

                             Product p1 = (Product) is.readObject();

                             System.out.println(p1.toString());

                     }

}

 

·         The program simply opens the previously created data file, reads its data into the Product object, and prints its contents. When we run the program, output similar to the following is shown:

             Product@9304b1

·         Whatever is printed to the console is definitely not what we want. We want the product’s price and tax to be printed to the console.

·         Therefore, we will now override the default toString method of the Product class. The modified Product class definition is shown here.

Modified Product Class

class Product implements Serializable {

                     private float price;

                     private float tax;

 

                     public Product(float price) {

                             this.price = price;

                             tax = (float) (price * 0.20);

                     }

 

                     public String toString() {

                             return ("Price:" + price + " Tax:" + tax);

                     }

}

 

Now, let’s run the ProductReader application again. We’ll see the following error printed to the console:

Exception in thread "main" java.io.InvalidClassException: Product; local class

incompatible: stream classdesc serialVersionUID = -4609301823165882715, local

class serialVersionUID = -3424249794808075076

 

This is because the Product state was saved with the previous version of the Product class. Java assigns a unique identifier (serialversionUID) to every serializable class during compilation.

Thus, when we change the class definition, the object state information we had saved becomes incompatible with the new version of the class. This problem can be solved by adding the serialversionUID (which represents the stream unique identifier, or SUID) of the original class to the modified class definition. To determine the serialversionUID of a class, run the following command on the command prompt:

C:\360\io> serialver Product

Product: static final long serialVersionUID = -3424249794808075076L;

 

The utility gives us the serial version UID of the specified class. Copy the following statement into the new class definition:

static final long serialVersionUID = -3424249794808075076L;

Note that the ID generated on our machine will differ from what’s shown here. The modified Product class is shown in here:

class Product implements Serializable {

                     private float price;

                     private float tax;

                     static final long serialVersionUID = -3424249794808075076L;

 

                     public Product(float price) {

                             this.price = price;

                             tax = (float) (price * 0.20);

                     }

 

                     public String toString() {

                             return ("Price:" + price + " Tax:" + tax);

                     }

}

 

Recompile the Product class and re-run our ProductReader application. We will see the following output:

Price:100.0 Tax:20.0

The new class now uses the serialVersionUID of the earlier class. The compiler in this case does not generate the new ID for the modified class. Thus, the objects created with earlier versions now remain compatible with the newer versions as far as serialization is concerned.

Note: If we:

·         modify the class fields

·         add a new field

·         delete an existing field

the object’s saved state will become incompatible with the earlier version of the class. The earlier objects will still be readable with the modified class definitions as long as you maintain the same serialVersionUID across the different versions of a class. Our program should take care of the newly added fields or the missing fields when we read the data stored in earlier versions.


Don't want to miss updates? Please click the below button!

Follow MindStick