Charles Bryant Charles Bryant - 2 months ago 6
Java Question

What is the best practice for saving and retrieving a java object?

I am writing a java application that is a document store. I create my objects and save them to disk with serialization.

I came across an error when I was loading my objects from disk, but I had actually changed my base object that it was serializing to.

This seems like a bad way to manage storing objects, if I updated my software with changes to my base object all of my objects on disk would be invalid.

Is there and guidance or best practice in dealing this issue? Or is there a better way for me to save my data?

VGR VGR
Answer

You’ll want to read the Java Object Serialization Specification, specifically the Compatible Java Type Evolution section and the section immediately following it, Type Changes Affecting Serialization.

Section 1.10 states:

For serializable objects, sufficient information is kept to restore those objects even if a different (but compatible) version of the implementation of the class is present.

As a developer, you are responsible to making sure that changes to your classes do not conflict with earlier serialized versions. It’s not as hard as you might think. Mostly, you need to avoid incompatible changes:

  • Do not delete a field. If it is no longer used, deprecate it. (This includes making an instance field a static field; static fields are not serialized, so this is equivalent to removing it as far as serialization is concerned.)
  • Do not change a field’s type.

You can save additional data by adding a void writeObject(ObjectOutputStream) method to your class, and you can perform additional initialization by adding a void readObject(ObjectInputStream) method. These are described in detail in the documentation for Serializable. Note that the first line of code in those methods should be stream.defaultWriteObject() and stream.defaultReadObject(), respectively.

readObject is important when you add fields to a class, if you want those fields to be initialized. For instance, if you have a new field which you always want to be non-null:

private List<String> names = new ArrayList<>();

Any older instance which was serialized without that field present will be deserialized with that field completely unset—that is, it will remain null (since all Object fields are null when an object is created, unless explicitly initialized). You can use readObject to account for this:

private void readObject(ObjectInputStream stream)
throws IOException,
       ClassNotFoundException {

    // First, do default serialization 
    stream.defaultReadObject();

    if (this.names == null) {
        this.names = new ArrayList<>();
    }
}