Skip to main content

By clicking Submit, you agree to the developerWorks terms of use.

The first time you sign into developerWorks, a profile is created for you. Select information in your developerWorks profile is displayed to the public, but you may edit the information at any time. Your first name, last name (unless you choose to hide them), and display name will accompany the content that you post.

All information submitted is secure.

  • Close [x]

The first time you sign in to developerWorks, a profile is created for you, so you need to choose a display name. Your display name accompanies the content you post on developerworks.

Please choose a display name between 3-31 characters. Your display name must be unique in the developerWorks community and should not be your email address for privacy reasons.

By clicking Submit, you agree to the developerWorks terms of use.

All information submitted is secure.

  • Close [x]

Introduction to Java programming, Part 2: Constructs for real-world applications

More-advanced Java language features

J Steven Perry, Principal Consultant, Makoto Consulting Group, Inc.
Photo of J Steven Perry
J. Steven Perry is a software developer, architect, and general Java nut who has been developing software professionally since 1991. His professional interests range from the inner workings of the JVM to UML modeling and everything in between. Steve has a passion for writing and mentoring; he is the author of Java Management Extensions (O'Reilly), Log4j (O'Reilly), and the IBM developerWorks articles "Joda-Time" and OpenID for Java Web applications." In his spare time, he hangs out with his three kids, rides his bike, and teaches yoga.

Summary:  In Part 1 of this tutorial, professional Java™ programmer J. Steven Perry introduced the Java language syntax and libraries you need to write simple Java applications. Part 2, still geared toward developers new to Java application development, introduces the more-sophisticated programming constructs required for building complex, real-world Java applications. Topics covered include exception handling, inheritance and abstraction, regular expressions, generics, Java I/O, and Java serialization.

View more content in this series

Date:  19 Aug 2010
Level:  Introductory PDF:  A4 and Letter (904 KB | 53 pages)Get Adobe® Reader®

Comments:  

Java serialization

Java serialization is another one the Java platform's essential libraries. Serialization is primarily used for object persistence and object remoting, two use cases where you need to be able to take a snapshot of the state of an object and then reconstitute it at a later time. This section gives you a taste of the Java Serialization API and shows how to use it in your programs.

What is object serialization?

Serialization is a process where the state of an object and its metadata (such as the object's class name and the names of its attributes) are stored in a special binary format. Putting the object into this format — serializing it — preserves all the information necessary to reconstitute (or deserialize) the object whenever you need to do so.

There are two primary use cases for object serialization:

  • Object persistence means storing the object's state in a permanent persistence mechanism such as a database.
  • Object remoting means sending the object to another computer or system.

java.io.Serializable

The first step to making serialization work is to enable your objects to use the mechanism. Every object you want to be serializable must implement an interface called java.io.Serializable:

import java.io.Serializable;
public class Person implements Serializable {
// etc...
}

The Serializable interface marks the objects of the Person class to the runtime as serializable. Every subclass of Person will also be marked as serializable.

Any attributes of an object that are not serializable will cause the Java runtime to throw a NotSerializableException if it tries to serialize your object. You can manage this by using the transient keyword to tell the runtime not to try to serialize certain attributes. In that case, you are responsible for making sure the attributes are restored so that your object will function properly.

Serializing an object

Now you'll try an example that combines what you've just learned about Java I/O with what you're learning now about serialization.

Suppose you create and populate a Manager object (recall that Manager is in the inheritance graph of Person, which is serializable) and then want to serialize that object to an OutputStream, in this case to a file. That process is shown in Listing 29:


Listing 29. Serializing an object
Manager m = new Manager();
m.setEmployeeNumber("0001");

m.setGender(Gender.FEMALE);
m.setAge(29);
m.setHeight(170);
m.setName("Mary D. Boss");
m.setTaxpayerIdentificationNumber("123-45-6789");
log.info("About to write object using serialization... object looks like:");
m.printAudit(log);
try {
 String filename = "Manager-" + m.hashCode() + ".ser";
 ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(filename));
 oos.writeObject(m);
 log.info("Wrote object...");
} catch (Exception e) {
 log.log(Level.SEVERE, "Caught Exception processing object", e);
}

The first step is to create the object and set some attribute values. Next, you create an OutputStream, in this case a FileOutputStream, and then call writeObject() on that stream. writeObject() is a method that uses Java serialization to serialize an object to the stream.

In this example, you are storing the object in a file, but this same technique is used for any type of serialization.

Deserializing an object

The whole point of serializing an object is to be able to reconstitute, or deserialize, it. Listing 30 reads the file you've just serialized and deserializes its contents, thus restoring the state of the Manager object:


Listing 30. Deserializing an object

Manager m = new Manager();
m.setEmployeeNumber("0001");
m.setGender(Gender.FEMALE);
m.setAge(29);
m.setHeight(170);
m.setName("Mary D. Boss");
m.setTaxpayerIdentificationNumber("123-45-6789");
log.info("About to write object using serialization... object looks like:");
m.printAudit(log);
try {
 String filename = "Manager-" + m.hashCode() + ".ser";
 ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(filename));
 oos.writeObject(m);
 log.info("Wrote object...");

 ObjectInputStream ois = new ObjectInputStream(new FileInputStream(filename));
 m = (Manager)ois.readObject();
 log.info("Read object using serialization... object looks like:");
 m.printAudit(log);
} catch (Exception e) {
 log.log(Level.SEVERE, "Caught Exception processing object", e);
}

For most application purposes, marking your objects as serializable is all you'll ever need to worry about when it comes to serialization. In cases where you do need to serialize and deserialize your objects explicitly, you can use the technique shown in Listings 29 and 30. But as your application objects evolve, and you add and remove attributes to and from them, serialization takes on a new layer of complexity.


serialVersionUID

Back in the early days of middleware and remote object communication, developers were largely responsible for controlling the "wire format" of their objects, which caused no end of headaches as technology began to evolve.

Suppose you added an attribute to an object, recompiled it, and redistributed the code to every machine in an application cluster. The object would be stored on a machine with one version of the serialization code, but accessed by other machines that might have a different version of the code. When those machines tried to deserialize the object, bad things often happened.

Java serialization metadata — the information included in the binary serialization format — is sophisticated and solves many of the problems that plagued early middleware developers. But it cannot solve every problem.

Java serialization uses a property called serialVersionUID to help you deal with different versions of objects in a serialization scenario. You don't need to declare this property on your objects; by default, the Java platform uses an algorithm that computes a value for it based on your class's attributes, its class name, and position in the local galactic cluster. Most of the time, that works fine. But if you add or remove an attribute, that dynamically generated value will change, and the Java runtime will throw an InvalidClassException.

To avoid this, you should get in the habit of explicitly declaring a serialVersionUID:

import java.io.Serializable;
public class Person implements Serializable {
 private static final long serialVersionUID = 20100515;
// etc...
}

I recommend using some kind of scheme for your serialVersionUID version number (I've used the current date in the example above), and you should declare it private static final and of type long.

You may be wondering when to change this property. The short answer is that you should change it whenever you make an incompatible change to the class, which usually means you've removed an attribute. If you have one version of the object on one machine that has the attribute removed, and the object gets remoted to a machine with a version of the object where the attribute is expected, then things can get weird.

As a rule of thumb, any time you add or remove features (meaning attributes and methods) of a class, you should change its serialVersionUID. Better to get an InvalidClassException on the other end of the wire than an application bug that's due to an incompatible class change.

11 of 14 | Previous | Next

Comments



static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=1
Zone=Java technology
ArticleID=508383
TutorialTitle=Introduction to Java programming, Part 2: Constructs for real-world applications
publish-date=08192010
author1-email=steve@makotoconsulting.com
author1-email-cc=jaloi@us.ibm.com