When should I change SerialUID?

I have a bunch of serialized classes. Usually I generated serial UIDs for all of them, since Java rules are pretty restrictive and recreate serial numbers with almost any changes. But this led me to the question that I could not find the answer on the Internet:

When does it make sense to break backward compatibility and manually change the UID Serial Version in a class?

+4
source share
3 answers

Section 5.6 of Java Spec helps here: http://download.oracle.com/javase/6/docs/platform/serialization/spec/version.html#6678

5.6 Type Changes Affecting Serialization

With these concepts, we can now describe how design will cope with various cases of a developing class. Cases are described in stream terms written by some version of the class. When a stream is read back by the same version of the class, there is no loss of information or functionality. A thread is the only source of information about the source class. His class descriptions, while a subset of the original class description, are sufficient to match the data in the stream with the version of the class restored.

The description refers to the perspective of the stream that is read in to recreate an earlier or later version of the class. In the language of RPC systems, this “receiver does the right thing” is a system. The writer writes his data in the most suitable form, and the receiver must interpret this information to extract the parts she needs and fill in the parts that are not available.

5.6.1 Incompatible changes

Incompatible class changes are those changes for which the compatibility guarantee cannot be maintained. Incompatible changes that may occur during class development are as follows:

  • Delete fields. If the field is deleted in the class, the recorded stream will not contain its values. When a stream is read from an earlier class, the field value will be set to the default value because there is no value in the stream. However, this default value may adversely affect the ability of an earlier version to fulfill its contract.
  • Move classes up or down the hierarchy. This is not possible because the data in the stream is displayed in the wrong sequence.
  • Changing a non-static field to a static or non-transition field for a transition period. When using default serialization, this change is equivalent to removing a field from the class. This version of the class will not write this data to the stream, so it will not be readable by earlier versions of the class. Like when deleting a field, an earlier version field will be initialized to a default value, which may cause the class to fail in unexpected ways.
  • Changing the declared type of a primitive field. Each version of the class writes data with a declared type. Earlier versions of a class attempting to read a field will fail because the data type in the stream does not match the field type.
  • Changing the writeObject or readObject method so that it no longer writes or read the default field data or changes it so that it tries to write or read it when the previous version did not. By default, these fields should either appear or not appear in the stream.
  • Changing a class from Serializable to Externalizable, or vice versa, is an incompatible change, because the stream will contain data that is incompatible with the implementation of the available class.
  • Changing a class from a type other than an enumeration to an enumeration type, or vice versa, since the stream will contain data that is incompatible with the implementation of the available class.
  • Removing Serializable or Externalizable is an incompatible change, since when writing it will no longer provide the required fields with older versions of the class.
  • Adding a writeReplace or readResolve method to a class is incompatible if the behavior creates an object that is incompatible with any older version of the class.

5.6.2 Compatible Changes

Compatible class changes are handled as follows:

  • Adding fields. When the class being restored has a field that does not occur in the stream, this field in the object will be initialized with a default value for its type. If the class needs initialization, the class can provide a readObject method that can initialize the field for insecure values.
  • Adding classes - the stream will contain a hierarchy of types of each object in the stream. Comparing this hierarchy in a stream with the current class can detect additional classes. Since there is no information in the stream from which the object is initialized, the class fields will be initialized with default values.
  • Removing classes. Comparing the class hierarchy in the stream with the current class may find that the class has been deleted. In this case, the fields and objects corresponding to this class are read from the stream. Primitive fields are discarded, but objects that reference the remote class are created because they can be mentioned later in the stream. They will collect garbage when the thread collects garbage or reset.
  • Adding writeObject / readObject methods. If the version reading the stream has these methods, then readObject is expected to read the required data written to the default serialization stream as usual. It must first call defaultReadObject before reading any additional data. The writeObject method is expected, as usual, to call defaultWriteObject to write the required data, and then can write additional data.
  • Removing writeObject / readObject methods. If the class that reads the stream does not have these methods, the necessary data will be read by default serialization, and additional data will be discarded.
  • Adding java.io.Serializable is equivalent to adding types. There will be no values ​​in the stream for this class, so its fields will be initialized with default values. Subclass support for non-serializable classes requires that the class supertype have a no-arg constructor and the class itself will be initialized to default values. If the no-arg constructor is not available, an InvalidClassException is thrown.
  • Changing field access — public, package, protected, and private access modifiers do not affect serialization ability to assign values ​​to fields.
  • Changing a field from static to non-static or transitional to non-transitional - if you rely on the default serialization to calculate serializable fields, this change is equivalent to adding a field to the class. The new field will be written to the stream, but earlier the classes ignore the value, since serialization will not assign values ​​to static or transition fields.
+3
source

Never. You must organize yourself so that classes have the same serialVersionUID throughout your life. You must (a) resist serialization-incompatible class changes; (b) write your own readObject () / writeObject () / readResolve () / writeReplace () objects to preserve the original serialization format and determine the explicit serialVersionUID at the beginning of the class’s life cycle. The moment you change this value, you have a huge headache in your arms. Plan to avoid this.

+3
source

From JavaDoc Serializable Interface :

Serialization of serialization associates each serializable class with a version number called serialVersionUID, which is used during deserialization to ensure that the sender and receiver of the serialized object have loaded classes for this object that are compatible with serialization .

I think this is a good hint to answer your question: as soon as you change the class so that serialization is affected (for example, adding / removing / changing serialized class members), you really have to change the value of serialVersionUID .

+2
source

All Articles