Skip to main content

Can 'transient' variables be declared as 'final' or 'static'?

Java's serialization provides an elegant, and easy to use mechanism for making an object's state persistent. While controlling object serialization, we might have a particular object data member that we do not want the serialization mechanism to save.

The modifier transient can be applied to field members of a class to turn off serialization on these field members. Every field marked as transient will not be serialized. You use the transient keyword to indicate to the Java virtual machine that the transient variable is not part of the persistent state of an object.

The transient modifier applies to variables only.

Like other variable modifiers in the Java system, you use transient in a class or instance variable declaration like this:

class TransientExample {
    transient int hobo;
    . . .
}

This statement declares an integer variable named hobo that is not part of the persistent state of the TransientExample class.

Java classes often hold some globally relevant value in a static class variable. The static member fields belong to class and not to an individual instance. The concept of serialization is concerned with the object's current state. Only data associated with a specific instance of a class is serialized, therefore static member fields are ignored during serialization (they are not serialized automatically), because they do not belong to the serialized instance, but to the class. To serialize data stored in a static variable one must provide class-specific serialization.

Surprisingly, the java compiler does not complaint if you declare a static member field as transient. However, there is no point in declaring a static member field as transient, since transient means: "do not serialize", and static fields would not be serialized anyway.

On the other hand, an instance member field declared as final could also be transient, but if so, you would face a problem a little bit difficult to solve: As the field is transient, its state would not be serialized, it implies that, when you deserialize the object you would have to initialize the field manually, however, as it is declared final, the compiler would complaint about it.

For instance, maybe you do not want to serialize your class' logger, then you declared it this way:
private transient final Log log = LogFactory.getLog(EJBRefFactory.class);

Now, when you deserialize the class your logger will be a null object, since it was transient. Then you should initialize the logger manually after serialization or during the serialization process. But you can't, because logger is a final member as well.

There is just one exception to this rule, and it is when the transient final field member is initialized to a constant expression as those defined in the  JLS 15.28. Hence, field members declared this way would hold their constant value expression even after deserializing the object. I guess that is so because the value of the final field is actually a constant expression as described by the JLS.

This example shows transient final fields that would hold their constant values even after deserializing the object:

class X implements Serializable {
  transient static String transientStaticVar = "transientStaticVar";
  transient final String transientFinalVar = "transientFinalVar";
  transient static final String transientStaticFinalVar 
       = "transientStaticFinalVar";
  transient String transientVar = "transientVar";
}

public class  Test {
  public static void main( String[] args ){
    try { 
      ObjectOutputStream o = new ObjectOutputStream( 
                      new FileOutputStream("logInfo.out")); 
      X a = new X();
      System.out.println("Before Serialization ...");
      System.out.println("transientStaticVar = " + X.transientStaticVar); 
      System.out.println("transientFinalVar = " + a.transientFinalVar); 
      System.out.println("transientStaticFinalVar = " +
          X.transientStaticFinalVar); 
      System.out.println("transientVar = " + a.transientVar); 
      o.writeObject(a); 
      o.close(); 
      X.transientStaticVar = "newTransientStaticVar";
    } 
    catch(Exception e) {
      //deal with exception
    } 

    try {
 
      ObjectInputStream in =new ObjectInputStream(
                             new FileInputStream("logInfo.out")); 
      X x = (X)in.readObject(); 
      System.out.println("After Serialization ...");
      System.out.println("transientStaticVar = " + X.transientStaticVar); 
      System.out.println("transientFinalVar = " + x.transientFinalVar); 
      System.out.println("transientStaticFinalVar = " + 
           X.transientStaticFinalVar); 
      System.out.println("transientVar = " + x.transientVar); 
    } 
    catch(Exception e) {
      //deal with exception
    } 
  
  }
 
}

The output results are:
Before Serialization ...
transientStaticVar = transientStaticVar
transientFinalVar = transientFinalVar
transientStaticFinalVar = transientStaticFinalVar
transientVar = transientVar
After Serialization ...
transientStaticVar = newTransientStaticVar
transientFinalVar = transientFinalVar
transientStaticFinalVar = transientStaticFinalVar
transientVar = null

If we change the way to initialize final variables, you have different variable and you can do not initialize them during the serialization process because they are final.

class X implements Serializable {
  transient static String transientStaticVar;
  transient final String transientFinalVar;
  transient static final String transientStaticFinalVar;
  transient String transientVar = "transientVar";
  
  static {
    transientStaticVar = "transientStaticVar" 
    transientStaticFinalVar = "transientStaticFinalVar";
  }
  
  {
    transientFinalVar = "transientFinalVar";
  }

}

public class  Test {
  public static void main( String[] args ){
    try { 
      ObjectOutputStream o = new ObjectOutputStream( 
                      new FileOutputStream("logInfo.out")); 
      X a = new X();
      System.out.println("Before Serialization ...");
      System.out.println("transientStaticVar = " + X.transientStaticVar); 
      System.out.println("transientFinalVar = " + a.transientFinalVar); 
      System.out.println("transientStaticFinalVar = " + 
          X.transientStaticFinalVar); 
      System.out.println("transientVar = " + a.transientVar); 
      o.writeObject(a); 
      o.close(); 
      X.transientStaticVar = "newTransientStaticVar";
    } 
    catch(Exception e) {
      //deal with exception
    } 

    try {
 
      ObjectInputStream in =new ObjectInputStream(
                             new FileInputStream("logInfo.out")); 
      X x = (X)in.readObject(); 
      System.out.println("After Serialization ...");
      System.out.println("transientStaticVar = " + X.transientStaticVar); 
      System.out.println("transientFinalVar = " + x.transientFinalVar); 
      System.out.println("transientStaticFinalVar = " + 
          X.transientStaticFinalVar); 
      System.out.println("transientVar = " + x.transientVar); 
    } 
    catch(Exception e) {
      //deal with exception
    } 
  
  }
 
}
The output results are:
Before Serialization ...
transientStaticVar = transientStaticVar
transientFinalVar = transientFinalVar
transientStaticFinalVar = transientStaticFinalVar
transientVar = transientVar
After Serialization ...
transientStaticVar = newTransientStaticVar
transientFinalVar = null
transientStaticFinalVar = transientStaticFinalVar
transientVar = null


Surprisingly, the java compiler does not complaint if you declare a static member field as transient or a final member field as transient in your classes. These should be compile-time errors. Because a "transient" part of an object's state is assumed to be changing within each instance, it can not be static or final. Similarly, a "volatile" variable cannot be final (constant). This restriction matters only in the future, though, when transient and volatile are actually used by Java.

Comments

Popular posts from this blog

Asynchronous Vs. Synchronous Communications

Synchronous (One thread):   1 thread -> |<---A---->||<----B---------->||<------C----->| Synchronous (multi-threaded):   thread A -> |<---A---->| \ thread B ------------> ->|<----B---------->| \ thread C ----------------------------------> ->|<------C----->|

WebSphere MQ Interview Questions

What is MQ and what does it do? Ans. MQ stands for MESSAGE QUEUEING. WebSphere MQ allows application programs to use message queuing to participate in message-driven processing. Application programs can communicate across different platforms by using the appropriate message queuing software products. What is Message driven process? Ans . When messages arrive on a queue, they can automatically start an application using triggering. If necessary, the applications can be stopped when the message (or messages) have been processed. What are advantages of the MQ? Ans. 1. Integration. 2. Asynchrony 3. Assured Delivery 4. Scalability. How does it support the Integration? Ans. Because the MQ is independent of the Operating System you use i.e. it may be Windows, Solaris,AIX.It is independent of the protocol (i.e. TCP/IP, LU6.2, SNA, NetBIOS, UDP).It is not required that both the sender and receiver should be running on the same platform What is Asynchrony? Ans. With messag...

Advantages & Disadvantages of Synchronous / Asynchronous Communications?

  Asynchronous Communication Advantages: Requests need not be targeted to specific server. Service need not be available when request is made. No blocking, so resources could be freed.  Could use connectionless protocol Disadvantages: Response times are unpredictable. Error handling usually more complex.  Usually requires connection-oriented protocol.  Harder to design apps Synchronous Communication Advantages: Easy to program Outcome is known immediately  Error recovery easier (usually)  Better real-time response (usually) Disadvantages: Service must be up and ready. Requestor blocks, held resources are “tied up”.  Usually requires connection-oriented protocol