Recommended: Sing it, brah! 5 fabulous songs for developers
JW's Top 5
Optimize with a SATA RAID Storage Solution
Range of capacities as low as $1250 per TB. Ideal if you currently rely on servers/disks/JBODs
Page 5 of 6
10 ObjectOutputStream out = new ObjectOutputStream(...); 20 MyObject obj = new MyObject(); // must be Serializable 30 obj.setState(100); 40 out.writeObject(obj); // saves object with state = 100 50 obj.setState(200); 60 out.writeObject(obj); // does not save new object state
There are two ways to control that situation. First, you could make sure to always close the stream after a write call, ensuring
the new object is written out each time. Second, you could call the ObjectOutputStream.reset() method, which would tell the stream to release the cache of references it is holding so all new write calls will actually
be written. Be careful, though -- the reset flushes the entire object cache, so all objects that have been written could be rewritten.
With our second gotcha, imagine you create a class, instantiate it, and write it out to an object stream. That flattened object sits in the file system for some time. Meanwhile, you update the class file, perhaps adding a new field. What happens when you try to read in the flattened object?
Well, the bad news is that an exception will be thrown -- specifically, the java.io.InvalidClassException -- because all persistent-capable classes are automatically given a unique identifier. If the identifier of the class does
not equal the identifier of the flattened object, the exception will be thrown. However, if you really think about it, why
should it be thrown just because I added a field? Couldn't the field just be set to its default value and then written out
next time?
Yes, but it takes a little code manipulation. The identifier that is part of all classes is maintained in a field called serialVersionUID. If you wish to control versioning, you simply have to provide the serialVersionUID field manually and ensure it is always the same, no matter what changes you make to the classfile. You can use a utility
that comes with the JDK distribution called serialver to see what that code would be by default (it is just the hash code of the object by default).
Here is an example of using serialver with a class called Baz:
> serialver Baz > Baz: static final long serialVersionUID = 10275539472837495L;
Simply copy the returned line with the version ID and paste it into your code. (On a Windows box, you can run that utility
with the - show option to simplify the copy and paste procedure.) Now, if you make any changes to the Baz class file, just ensure that same version ID is specified and all will be well.
The version control works great as long as the changes are compatible. Compatible changes include adding or removing a method
or a field. Incompatible changes include changing an object's hierarchy or removing the implementation of the Serializable interface. A complete list of compatible and incompatible changes is given in the Java Serialization Specification (see Resources).
Our third gotcha: the default mechanism, although simple to use, is not the best performer. I wrote out a Date object to a file 1,000 times, repeating that procedure 100 times. The average time to write out the Date object was 115 milliseconds. I then manually wrote out the Date object, using standard I/O the same number of iterations; the average time was 52 milliseconds. Almost half the time! There
is often a trade-off between convenience and performance, and serialization proves no different. If speed is the primary consideration
for your application, you may want to consider building a custom protocol.