Transience

The Java language's transient keyword isn't as well known as for, class, synchronized, and other familiar keywords; which makes it a perfect candidate for inclusion in a job interview questionnaire. In this post, I explain transient.

The purpose of transient

Q: What does the transient keyword accomplish?

A: The transient keyword prevents the values of instance fields that are declared with this keyword from being persisted when an object is serialized (written as a sequence of bytes to some destination). The values of such instance fields shouldn't be persisted and later restored when an object is deserialized (reconstituted from a sequence of bytes read from some source). For example, if your object has a field of type java.io.InputStream, you don't serialize this field's reference because the stream won't be open for input when you deserialize the object -- the stream source (e.g., a file) might not exist.

Using transient

Q: How do I use transient?

A: Include the transient modifier in the instance field's declaration. Listing 1 presents a small demonstration.

Listing 1. Serializing and deserializing a ClassLib object

import java.io.DataInputStream;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;

class ClassLib implements Serializable
{
   private transient InputStream is;

   private int majorVer;
   private int minorVer;

   ClassLib(InputStream is) throws IOException
   {
      System.out.println("ClassLib(InputStream) called");
      this.is = is;
      DataInputStream dis;
      if (is instanceof DataInputStream)
         dis = (DataInputStream) is;
      else
         dis = new DataInputStream(is);
      if (dis.readInt() != 0xcafebabe)
         throw new IOException("not a .class file");
      minorVer = dis.readShort();
      majorVer = dis.readShort();
   }

   int getMajorVer()
   {
      return majorVer;
   }

   int getMinorVer()
   {
      return minorVer;
   }

   void showIS()
   {
      System.out.println(is);
   }
}

public class TransDemo
{
   public static void main(String[] args) throws IOException
   {
      if (args.length != 1)
      {
         System.err.println("usage: java TransDemo classfile");
         return;
      }
      ClassLib cl = new ClassLib(new FileInputStream(args[0]));
      System.out.printf("Minor version number: %d%n", cl.getMinorVer());
      System.out.printf("Major version number: %d%n", cl.getMajorVer());
      cl.showIS();

      try (FileOutputStream fos = new FileOutputStream("x.ser");
           ObjectOutputStream oos = new ObjectOutputStream(fos))
      {
         oos.writeObject(cl);
      }

      cl = null;

      try (FileInputStream fis = new FileInputStream("x.ser");
           ObjectInputStream ois = new ObjectInputStream(fis))
      {
         System.out.println();
         cl = (ClassLib) ois.readObject();
         System.out.printf("Minor version number: %d%n", cl.getMinorVer());
         System.out.printf("Major version number: %d%n", cl.getMajorVer());
         cl.showIS();
      }
      catch (ClassNotFoundException cnfe)
      {
         System.err.println(cnfe.getMessage());
      }
   }
}

Listing 1 declares ClassLib and TransDemo classes. ClassLib is the skeletal beginnings of a library for reading Java classfiles, and implements the java.io.Serializable interface so that its instances can be serialized and deserialized. TransDemo is an application class that serializes and deserializes a ClassLib instance.

ClassLib declares its is instance field transient because it makes no sense to serialize an input stream (as previously explained). In fact, if this field wasn't transient, a java.io.NotSerializableException would be thrown when deserializing x.ser's contents because InputStream doesn't implement the Serializable interface.

Compile Listing 1: javac TransDemo.java. Now, run this application with TransDemo.class as its single argument: java TransDemo TransDemo.class. You should observe output similar to the following:

ClassLib(InputStream) called
Minor version number: 0
Major version number: 51
java.io.FileInputStream@79f1e0e0

Minor version number: 0
Major version number: 51
null

This output shows that no constructor is invoked when the object is reconstituted. Furthermore, is assumes its default null value. In contrast, majorVer and minorVer are assigned the values they held when the ClassLib object was serialized.

Class fields and transient

Q: Can transient be used with class fields?

A: For an answer to this question, consider Listing 2.

Listing 2. Serializing and deserializing a Foo object

import java.io.DataInputStream;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;

class Foo implements Serializable
{
   public static int w = 1;
   public static transient int x = 2;
   public int y = 3;
   public transient int z = 4;
}

public class TransDemo
{
   public static void main(String[] args) throws IOException
   {
      Foo foo = new Foo();
      System.out.printf("w: %d%n", Foo.w);
      System.out.printf("x: %d%n", Foo.x);
      System.out.printf("y: %d%n", foo.y);
      System.out.printf("z: %d%n", foo.z);
      try (FileOutputStream fos = new FileOutputStream("x.ser");
           ObjectOutputStream oos = new ObjectOutputStream(fos))
      {
         oos.writeObject(foo);
      }

      foo = null;

      try (FileInputStream fis = new FileInputStream("x.ser");
           ObjectInputStream ois = new ObjectInputStream(fis))
      {
         System.out.println();
         foo = (Foo) ois.readObject();
         System.out.printf("w: %d%n", Foo.w);
         System.out.printf("x: %d%n", Foo.x);
         System.out.printf("y: %d%n", foo.y);
         System.out.printf("z: %d%n", foo.z);
      }
      catch (ClassNotFoundException cnfe)
      {
         System.err.println(cnfe.getMessage());
      }
   }
}

Listing 2 is similar to Listing 1. However, instead of serializing and deserializing a ClassLib object, a Foo object is serialized and deserialized. Furthermore, Foo contains a pair of class fields, w and x, as well as instance fields, y and z.

Compile Listing 2 (javac TransDemo.java) and run this application (java TransDemo). You should observe the following output:

w: 1
x: 2
y: 3
z: 4

w: 1
x: 2
y: 3
z: 0

This output tells us that instance field y was serialized but not z, which was marked transient. However, it doesn't tell us whether class fields w and x were serialized and then deserialized, or simply initialized in the normal class-initialization manner when the serialized Foo object was deserialized. For an answer, we need to investigate x.ser's contents.

A hexadecimal dump of x.ser provides the following output:

00000000 AC ED 00 05 73 72 00 03 46 6F 6F FC 7A 5D 82 1D ....sr..Foo.z]..
00000010 D2 9D 3F 02 00 01 49 00 01 79 78 70 00 00 00 03 ..?...I..yxp....

Thanks to JavaWorld's The Java serialization algorithm revealed article, we can discover what this output means:

  • AC ED is the stream magic number that identifies the byte sequence as the serialization protocol.
  • 00 05 is the stream version number.
  • 73 indicates that this is a new Object.
  • 72 indicates that this is a new class.
  • 00 03 indicates the length of the class name (3).
  • 46 6F 6F indicates the class name (Foo).
  • FC 7A 5D 82 1D D2 9D 3F indicates the class's serial version identifier.
  • 02 indicates that the object supports serialization.
  • 00 01 indicates the number of fields in this class (1).
  • 49 is the field type code (0x49, or I, represents int).
  • 00 01 indicates the length of the field name (1).
  • 79 indicates the field name (y).
  • 78 indicates the end of the object's optional block data.
  • 70 indicates that we have reached the top of the class hierarchy.
  • 00 00 00 03 indicates the value of y (3).

It should be obvious that only the single y instance field was serialized. z wasn't serialized because it's transient. Also, w and x weren't serialized because class fields aren't serialized even though they can be marked transient.

What's next?

Next time, I present a pair of useful utility applications for dumping a file in hexadecimal format. One application outputs this file dump to the standard output, whereas the second application outputs this file dump to its GUI window.

download
Get the source code for this post's applications. Created by Jeff Friesen for JavaWorld

The following software was used to develop the post's code:

  • 64-bit JDK 7u6

The post's code was tested on the following platform(s):

  • JVM on 64-bit Windows 7 SP1
Notice to our Readers
We're now using social media to take your comments and feedback. Learn more about this here.