views:

1138

answers:

4

In Java, static and transient fields are not serialized. However, I found out that initialization of static fields causes the generated serialVersionUID to be changed. For example, static int MYINT = 3; causes the serialVersionUID to change. In this example, it makes sense because different versions of the class would get different initial values. Why does any initialization change the serialVersionUID? For example, static String MYSTRING = System.getProperty("foo"); also causes the serialVersionUID to change.

To be specific, my question is why does initialization with a method cause the serialVersionUID to change. The problem I hit is that I added a new static field that was initialized with a system property value (getProperty). That change caused a serialization exception on a remote call.

A: 

If I read the spec correctly the automatic serialVersionUID shouldn't change if you change the value of a static of transient field. Take a look at Chapter 5.6 of the Spec.

However, if you think about this a bit - you start by serializing an object that has static int MYINT = 3, when you then deserialize the class you expect to get the same object back, that is, with MYINT = 3. So, if you change the static initialization you would expect the serialVersionUID to change because you can't get the same object back again.

Anyways, keep this in all your serializable classes and you can control the serialVersionUID:

private static final long serialVersionUID = 7526472295622776147L;
Alfred B. Thordarson
A: 

I updated the question to be more clear. I understand why initialization with a literal changes the serialVersionUID but not why dynamic initialization changes it. If you initialzie with a method, the value, of course, may always be different.

Setting the serialVersionUID explicitly is fine in a subsequent version of the class only if you are sure that it is a safe change.

David G
+3  A: 

You can find some information about that in the bug 4365406 and in the algorithm for computing serialVersionUID. Basically, when changing the initialization of your static member with System.getProperty, the compiler introduces a new static property in your class referencing the System class (I assume that the System class was previously unreferenced in your class), and since this property introduced by the compiler is not private, it takes part in the serialVersionUID computation.

Morality: always use explicit serialVersionUID, you'll save some CPU cycles and some headaches :)

Damien B
+1  A: 

Automatic serialVersionUID is calculated based on members of a class. These can be shown for a class file using the javap tool in the Sun JDK.

In the case mentioned in the question, the member that is added/removed is the static initialiser. This appears as ()V in class files. The contents of the method can be disassembled using javap -c. You should be able to make out the System.getProperty("foo") call and assignment to MYSTRING. However an assignment with a string literal (or any compile-time constant as defined by the Java Language Specification) is supported directly by the class file, so removing the need for a static initialiser.

A common case for code targeting J2SE 1.4 (use -source 1.4 -target 1.4) or earlier is static fields to old Class instances which appear as class literals in source code (MyClass.class). The Class instance is looked up on demand with Class.forName, and the stored in a static field. It is this static field that disrupts the serialVersionUID. From J2SE 5.0, a variant of the ldc opcode gives direct support for class literals, removing the need for the synthetic field. Again, all this can be shown with javap -c.

Tom Hawtin - tackline