Tag Archives: JVM

Transient and final instance variables in Java

In Java, transient and final are two important keywords and it is legal to put them as modifier of instance variables.

But how does it behave when it comes to object serialization? Since it is transient, we can guess such variable are not serialized and therefore, when the object is deserialized, they are initialized to their default values, like for regular non final instance variable.

But in fact, it depends! See the code below:

public class Diplodocus implements Serializable {

	private final transient String s1 = "test";
	private final transient String s2;
	private final transient String s3 = new String("hello");
	private final transient String s4 = s1 + s1 + 1;
	private final transient int i1 = 7;
	private final transient int i2 = 7 * 3;
	private final transient int i3 = Integer.MIN_VALUE;
	private final transient int[] a1 = {1,2,3};

	public Diplodocus() {
		s2 = "s2";
	}

	public static void main(String[] args) {

		File f = new File("diplo");
		try {
			FileOutputStream fos = new FileOutputStream(f);
			ObjectOutputStream oos = new ObjectOutputStream(fos);
			oos.writeObject(new Diplodocus());
			oos.flush();
			oos.close();

			FileInputStream fis = new FileInputStream(f);
			ObjectInputStream ois = new ObjectInputStream(fis);
			Diplodocus diplo = (Diplodocus) ois.readObject();
			ois.close();
			System.out.println("s1 = "+diplo.s1);
			System.out.println("s2 = "+diplo.s2);
			System.out.println("s3 = "+diplo.s3);
			System.out.println("s4 = "+diplo.s4);
			System.out.println("i1 = "+diplo.i1);
			System.out.println("i2 = "+diplo.i2);
			System.out.println("i3 = "+diplo.i3);
			System.out.println("a1 = "+diplo.a1);

		} catch (IOException e) {
			e.printStackTrace();
		} catch (ClassNotFoundException e) {
			e.printStackTrace();
		}
	}
}

What we expect here is that the instance variables after the deserialization will be equals to the default initialization values : null for objects like Strings and 0 for the integers.

But, in fact the code has the following output:

s1 = test
s2 = null
s3 = null
s4 = testtest1
i1 = 7
i2 = 21
i3 = -2147483648
a1 = null

What happened?

During deserialization, the constructor of the object is not called. This is a special object instantiation process handled by the JVM. For regular non-final transient instance variable, they are assigned with their default values.

But what happened for final variables? There is a little trick.

If the final variable is blank, meaning that it is first initialized in the object constructor(s) (like on line 13), the deserialization will put the default value. That is why s2 = null.

On the contrary, for a final variable explicitly initialized, the value is reaffected, but only if this initializations is done in a certain manner, in fact, only if the initialization is done with what the Java specification calls a compile-time constant expression (§15.28).

Constant expressions are the ones you are allowed to use in the case labels of a switch statement. A constant expression is an expression, which can be resolved at compile-time with no ambiguity: somehow, it gives the guarantee that the resulting value will never change during the execution.

In our case, these are considered constant expressions and that is why the values are reaffected to the final variables (even if they are transient).

  • literals (like a String on line 3, or an integer on line 7)
  • + operation between literals and reference to a constant final variables (like on line 6)
  • * operation between integers (like on line 8)
  • reference to constants (like on line 9)

There are other types of constant expressions (e.g. cast operation to a primitive or a String). I suggest you to have a look at the specifications for more details1.

However, object instantiations are not considered to be constant expressions and that is why these are assigned to the default values (like on lines 5 and 10).

Conclusion

Be careful when you use transient final instance variable! You may expect to get attributes initialized or not according to the context. And the worst part is that, since they are final you cannot modify them directly afterwards.

  1. or simply check this: http://docs.oracle.com/javase/specs/jls/se7/html/jls-15.html#jls-15.28 []