Java transient Keyword with Serialization Examples

Learn the Java transient keyword with serialization examples, restoring fields in readObject(), static and final transient fields, Jackson, Gson and JPA.

Serializing a Session object with a transient cache field

The transient keyword marks a field that Java serialization must skip, so the field is not written to the byte stream and gets its default value when the object is read back. It applies only to fields of classes that implement Serializable, and the JVM ignores it everywhere else, although some libraries also honor it.

We mark fields as transient when they hold data that can be recomputed, such as a cache or a derived total, or objects that cannot be serialized, such as a database connection, a thread or a logger. Many JSON libraries also follow the modifier, which makes it a quick way to keep a field out of the output.

The following example serializes a session with a transient cache. The roundTrip() helper and the Session class come from section 1.

Session session = new Session("lokesh");
session.cache.put("theme", "dark");
Session restored = roundTrip(session);
String user = restored.user;            // "lokesh"
Map<String, String> cache = restored.cache;   // null

Notice that the restored cache is null, not an empty map, because the constructor does not run during deserialization. We look at how the keyword works, how to restore transient fields, the special cases of static and final fields, and how JSON libraries and JPA treat it.

1. How transient Works

Java serialization writes all non-static, non-transient fields of a Serializable object, following references to other objects. A transient field is left out, and when the object is read back, it holds the default value of its type, which is null, 0 or false.

Serializing a Session object with a transient cache field
The transient cache is not written, so it comes back as null
class Session implements Serializable {
    final String user;
    transient Map<String, String> cache = new HashMap<>();

    Session(String user) {
        this.user = user;
    }
}
@SuppressWarnings("unchecked")
static <T> T roundTrip(T object) throws Exception {
    ByteArrayOutputStream bytes = new ByteArrayOutputStream();
    try (ObjectOutputStream out = new ObjectOutputStream(bytes)) {
        out.writeObject(object);
    }
    try (ObjectInputStream in = new ObjectInputStream(new ByteArrayInputStream(bytes.toByteArray()))) {
        return (T) in.readObject();
    }
}

Deserialization creates the object without running its constructor or its field initializers, so = new HashMap<>() does not run either. Only the constructor of the first non-serializable parent class, often Object, is called.

2. Restoring transient Fields After Deserialization

A transient field often needs a valid value after deserialization. The standard place to set it is a private readObject() method, which the serialization runtime calls instead of its default reading code.

class Cart implements Serializable {
    private final List<Integer> prices = new ArrayList<>();
    private transient int total;         // derived from prices

    void add(int price) {
        prices.add(price);
        total += price;
    }

    int total() {
        return total;
    }

    @Serial
    private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException {
        in.defaultReadObject();          // reads prices
        total = prices.stream().mapToInt(Integer::intValue).sum();
    }
}
Cart cart = new Cart();
cart.add(30);
cart.add(12);
int restoredTotal = roundTrip(cart).total();   // 42

The @Serial annotation, added in Java 14, lets the compiler check the signature of methods such as readObject(). For a cache, a lazy getter that creates the map on first use is often simpler than readObject().

3. transient with static and final Fields

Static fields are never serialized, because they belong to the class, not to the object, so transient on a static field changes nothing. After deserialization, the field shows whatever value the class has in the current JVM.

A final transient field is a trap. Its initializer does not run during deserialization, so the field stays null or 0 and cannot be fixed in readObject() without reflection. The only exception is a constant, such as a String literal, because the compiler copies its value into every place that reads it.

class Report implements Serializable {
    final transient String title = "Sales";              // constant, inlined by javac
    final transient List<String> lines = new ArrayList<>();   // not a constant
}
Report copy = roundTrip(new Report());
String title = copy.title;              // "Sales"
List<String> lines = copy.lines;        // null

Older articles explain the title result by saying that serialization ignores transient for final fields. It does not, and the value is never read from the field at all. In practice, we avoid final transient fields unless they hold constants.

4. Real-World Uses of transient

The JDK itself uses the keyword where serialized data would be wrong or wasteful. In HashMap, the bucket array is transient, and the map writes only its keys and values. The reader rebuilds the buckets, because bucket positions depend on hash codes that can differ in another JVM.

  • Caches and computed values, which the reader rebuilds from the serialized data.
  • Resources such as connections, streams, threads and locks, which have no meaning in another process.
  • Loggers, although a static logger needs no modifier at all.
  • Sensitive values such as passwords, with the caveat that leaving a field out of a stream is not real security.

For secrets, transient only keeps the value out of one serialization format. The object still holds it in memory, and logs, heap dumps and other serializers may still expose it, so secrets need proper handling, such as encryption or not storing them at all.

5. transient in JSON Libraries and JPA

Other frameworks reuse the modifier with their own rules.

LibraryTreatment of a transient field
GsonExcluded by default
JacksonIgnored as a field, but still written if it has a public getter, unless MapperFeature.PROPAGATE_TRANSIENT_MARKER is enabled
JPA and HibernateNot stored in the database, the same as the @Transient annotation
Java recordsNot allowed, because record fields cannot be transient

JPA’s @Transient annotation and the transient keyword are not the same thing. The annotation excludes a field from persistence but keeps it in Java serialization, whereas the keyword excludes it from both, which matters when entities are also cached or sent between servers.

6. transient vs volatile

The two modifiers are often confused because both apply to fields and sound technical. They solve unrelated problems.

transientvolatile
AreaSerializationMultithreading
EffectField is skipped when the object is serializedWrites are visible to other threads at once, and reads see the latest write
Combined useAllowed, a field can be bothAllowed

Java serialization itself is a legacy technology with a long history of security problems, so new designs prefer JSON, Protocol Buffers or records with explicit formats. When we must read serialized data from outside, serialization filters limit which classes can be created.

7. transient Keyword FAQs

Developers who work with serialization ask these questions about transient.

7.1. What value does a transient field have after deserialization?

The default value of its type, null for objects, 0 for numbers and false for boolean, unless readObject() sets it. Field initializers and constructors do not run.

7.2. Can a local variable or a method be transient?

No. The modifier is allowed only on fields, because only fields are part of an object’s serialized state.

7.3. Does transient work with Externalizable?

No. An Externalizable class writes its own state in writeExternal(), so the modifier has no effect there, and the class decides what to write.

7.4. Is transient needed for fields that are not Serializable?

Yes, if the containing class is serialized. A field that refers to a non-serializable object makes writeObject() throw NotSerializableException unless the field is transient or null.

8. Conclusion

The transient keyword keeps a field out of Java serialization, and the field comes back with its default value because constructors and initializers do not run. A readObject() method or a lazy getter restores it.

Static fields are never serialized, and final transient fields stay empty unless they hold constants. Gson and JPA follow the keyword, Jackson follows it only partly, and none of this makes a password secure.

9. References

Happy Learning !!

Source Code on Github

Leave a Comment

  1. Sir can you explain why transient key word does’t work with static/final keyword in serialisation ?

  2. “`
    public class Bean1 implements Serializable {

    /**
    *
    */
    private static final long serialVersionUID = -9098687921796988767L;

    private int field1;
    private int field2;
    private transient final int field3 = 456*432/123;
    public Bean1(int field1, int field2) {
    super();
    this.field1 = field1;
    this.field2 = field2;
    }
    public int getField1() {
    return field1;
    }
    public void setField1(int field1) {
    this.field1 = field1;
    }
    public int getField2() {
    return field2;
    }
    public void setField2(int field2) {
    this.field2 = field2;
    }
    public int getField3() {
    return field3;
    }
    @Override
    public String toString() {
    return “Bean1 [field1=” + field1 + “, field2=” + field2 + “]”;
    }

    }
    “`
    this is the bean I used to test your 4th point in summary

    Whenever any final field/reference is evaluated as “constant expression”, it is serialized by JVM ignoring the presence of transient keyword.

    “`
    Bean1 o = new Bean1(123, 234);
    String filename = “file.ser”;

    // Serialization
    try
    {
    //Saving of object in a file
    FileOutputStream file = new FileOutputStream(filename);
    ObjectOutputStream out = new ObjectOutputStream(file);

    // Method for serialization of object
    out.writeObject(o);

    out.close();
    file.close();

    System.out.println(“Object has been serialized”);

    }

    catch(IOException ex)
    {
    System.out.println(“IOException is caught”);
    }
    Bean1 object1 = null;

    // Deserialization
    try
    {
    // Reading the object from a file
    FileInputStream file = new FileInputStream(filename);
    ObjectInputStream in = new ObjectInputStream(file);

    // Method for deserialization of object
    object1 = (Bean1)in.readObject();

    in.close();
    file.close();

    System.out.println(“Object has been deserialized “);
    System.out.println(object1.toString());
    }

    catch(IOException ex)
    {
    System.out.println(“IOException is caught”);
    }

    catch(ClassNotFoundException ex)
    {
    System.out.println(“ClassNotFoundException is caught”);
    }

    “`
    this is the main method code
    “`

    Object has been serialized
    Object has been deserialized
    Bean1 [field1=123, field2=234]
    “`
    this is the output I got and it ignored the transient field even though it is final and has a constant expression.

    waiting for your reply
    Thank you.

    • Sorry
      Actually it is my mistake
      Eclipse didn’t included the transient field
      I now included and it is fine.
      Thank you, you’d done a great work

  3. HI Lokesh,

    Could you explain why it would be a problem during deserialisation as you mentioned there will be loss of information for calculating new hash code.hash code would be calculated based on he method that we override for that specific class right !! Wont the JVM use the same method to calculate the hash code for entires in hash map after deserialisation is done.Do you mean to tell for the scenario where hash code method is not over riden to calculate the hash code and object class code(hash code calculates based on internal memory representation )is applied..please clarify this

    • Yes, you are right. I am talking about custom implementation of hashCode() method which may include fields which do not take into consideration while serialization. e.g static fields or simply transient fields. If hashCode() is using these fields then after de-serialization they would not be available.
      If you have not overridden hashCode() method then hashCode is definitely going to be different, because in JVM no two instances can have same hashcode, right?

Comments are closed.

About Us

HowToDoInJava provides tutorials and how-to guides on Java and related technologies.

It also shares the best practices, algorithms & solutions and frequently asked interview questions.