Saturday, October 11, 2008

A Few Details about serialver

I have recently been asked by several different people in several different situations about Java serialization, about the Serializable interface, about serialVersionUID, and about how to generate a serialVersionUID. In this blog entry, I'll focus on using the serialver tool provided with Sun's JDK to create a serialVersionUID.

There is more to making a class Serializable than just implementing the Serializable interface. In fact, Josh Bloch devotes an entire chapter of his Effective Java to serialization (Chapter 10 in the First Edition and Chapter 11 in the Second Edition). Bloch also discusses serialization in the JavaOne 2006 presentation Effective Java Reloaded. If you have a copy of either edition of Bloch's book (and I believe every developer who uses Java heavily should) refer to the first item in the Serialization chapter regarding the reasons for using Serializable lightly ("Implement Serializable judiciously"). You can find similar text on the subject at the Java Practices site under Implementing Serializable.

The Javadoc-generated API documentation for the Serializable interface "strongly recommends" creating an explicit serialVersionUID for classes implementing the Serializable interface. However, the example shown in the description sets the serialVersionUID to 42L. This example meets the criteria that it be a long that is static and private, but Bloch points out in Effective Java that this minimally qualifying serialVersionUID is not a good one.

Although it is recommended that an explicit serialVersionUID be added to each Serializable class, this is not technically required. Java will create a serialVersionUID dynamically when it is not explicitly stated in the class. No error or even warning is reported by default for a Serializable class without a servialVersionUID. However, one can use the non-standard javac nonstandard option -Xlint:serial with Sun's JDK to have warning messages printed out when a Serializable class does not possess an explicit serialVersionUID.

This concept is illustrated with the next two screen snapshots. Both screen snapshots show the compilation of a Serializable class (explicitly extends Serializable) that does not explicitly declare a serialVersionUID. The first image shows that the default behavior of the javac compiler is to not display any warnings about the missing serialVersionUID. The second screen image demonstrates that the -Xlint:serial nonstandard javac option does display warnings about a Serializable class that does not explicitly declare a serialVersionUID.

No Warnings of Errors without -Xlint:serial




-Xlint:serial Generates Warnings




Once we've taken Bloch's advice about implementing Serializable judiciously and have selected classes that do need to be serializable, one of the next steps is to generate a serialVersionUID. While these can be made up, it is a common practice to take advantage of the Sun-provided serialver tool to do this for us.

The serialver tool is located in the same directory in the JDK as java, javac, jar, javadoc, jconsole, jvisualvm, xjc (JAXB), policytool, rmiregistry, and several other Sun-provided Java tools many of us find ourselves using on a daily basis.

The serialver tool allows the developer to either enter the fully qualified Serializable class name whose serialVersionUID is desired on the command-line or with an HMI interface. To use the HMI, the developer uses the -show option. To use the command-line, the developer obviously omits the -show option, but the developer also specifies the fully qualified class name as the final argument to serialver. Whether the developer uses the command-line or the HMI, the classpath must be provided with the -classpath option (-cp does not work for serialver). This classpath must include the class whose serialVersionUID is desired as well as any classes used by that class.

There are a few nuances to note about serialver. First, you cannot specify the classname to be analyzed and the -show option at the same time. In other words, the ambiguous condition of implying command-line (by passing the name of the class to be evaluated) while at the same time explicitly requesting the HMI with the -show option is avoided by not allowing the two to be used together. Another thing to note about serialver is that it will report an error if you pass it a class that does not implement Serializable (probably a good thing because there is no use to adding a serialVersionUID data member to a non-Serializable class).

One last nuance is that if a Serializable class already has a serialVersionUID, serialver will simply report that serialVersionUID to the developer. This means that if you want to generate a new serialVersionUID after changing a class significantly enough to affect its serialization status, you will want to comment out the serialVersionUID data member, recompile the class, and then run serialver against the compiled class with that attribute commented out.

The next three screen snapshots illustrate some of these key principles of using serialver.

The first screen snapshot demonstrates the serialver HMI reporting a class that is not Serializable. The second image demonstrates that serialver will simply return any serialVersionUID already explicitly set for a class (I chose 42L because that is what is used in the Serializable documentation and in Bloch's treatment and it is also the answer to the ultimate question). The third screen snapshot demonstrates how to use both the command-line and HMI versions of serialver and also demonstrates that they both return the same serialVersionUID.


Cannot Run serialver on a Non-Serializable Class




serialver Returns Explicitly Declared serialVersionUID




Running serialver as HMI and on Command-Line




With the serialver tool, a Java developer can generate more meaningful serialVersionUIDs than he or she might otherwise create.



UPDATE (20 December 2008): A relatively popular article (23 "up" votes so far) on serialVersionUID has been published on JavaLobby. The article is called Don't Ignore serialVersionUID.

No comments: