Skip to content
Java serialization 5 min read

transient Keyword

When Java serializes an object, it writes every instance field to the byte stream by default. The transient keyword is your way of telling the JVM: “skip this field — don’t include it in the stream.” This is essential for passwords, session tokens, database connections, calculated caches, or any field that either shouldn’t leave the JVM or simply cannot be serialized.

What transient Does

Mark any instance field with transient and the serialization mechanism will:

  1. Ignore it during writeObject — the field’s value is never written to the byte stream.
  2. Restore it to its default value after readObject0 for numbers, false for booleans, null for object references.
import java.io.Serializable;

public class User implements Serializable {
    private static final long serialVersionUID = 1L;

    private String username;       // serialized normally
    private transient String password; // NEVER written to the stream

    public User(String username, String password) {
        this.username = username;
        this.password = password;
    }

    @Override
    public String toString() {
        return "User{username='" + username + "', password='" + password + "'}";
    }
}

After deserialization, password will be null regardless of what it was before.

Complete Round-Trip Example

Here is a self-contained example that serializes a User, then deserializes it to show the effect:

import java.io.*;

public class TransientDemo {
    public static void main(String[] args) throws Exception {
        User user = new User("alice", "s3cr3t!");
        System.out.println("Before: " + user);

        // Serialize to a byte array (same API as writing to a file)
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        try (ObjectOutputStream oos = new ObjectOutputStream(baos)) {
            oos.writeObject(user);
        }

        // Deserialize from that byte array
        ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
        try (ObjectInputStream ois = new ObjectInputStream(bais)) {
            User restored = (User) ois.readObject();
            System.out.println("After:  " + restored);
        }
    }
}

Output:

Before: User{username='alice', password='s3cr3t!'}
After:  User{username='alice', password='null'}

The username survives the round trip; password comes back as null.

Common Use Cases

ScenarioWhy transient?
Passwords / tokensSecurity — they must never persist to disk or travel over a wire
Database / network connectionsConnection, Socket — not serializable at all
Derived / cached valuesNo need to store what can be recalculated
Logger fieldsLogger instances are typically not serializable
UI componentsSwing/AWT components often aren’t serializable in all contexts

Warning: A java.io.NotSerializableException is thrown at runtime if a non-transient field holds an object whose class does not implement Serializable. Mark such fields transient to fix it.

transient with static Fields

transient only applies to instance fields. Static fields already belong to the class, not the object, so they are never serialized regardless of whether you add transient. Adding transient to a static field is allowed but redundant.

public class Config implements Serializable {
    private static final long serialVersionUID = 1L;

    // Both are never serialized, but only the second needs transient
    private static int instanceCount = 0;          // static — never serialized
    private transient int cachedHashCode = -1;     // transient — skipped
}

Restoring transient Fields After Deserialization

Because transient fields come back as their default values, you sometimes need to re-initialize them. You can do this by overriding the special readObject method:

import java.io.*;

public class ExpensiveCache implements Serializable {
    private static final long serialVersionUID = 1L;

    private String dataSource;
    private transient java.util.Map<String, String> cache; // rebuilt on demand

    public ExpensiveCache(String dataSource) {
        this.dataSource = dataSource;
        this.cache = new java.util.HashMap<>();
    }

    // Called automatically by ObjectInputStream
    private void readObject(ObjectInputStream in)
            throws IOException, ClassNotFoundException {
        in.defaultReadObject();         // restore all non-transient fields
        this.cache = new java.util.HashMap<>(); // re-initialize transient field
    }
}

Note: readObject must be private, take an ObjectInputStream, and call in.defaultReadObject() first. The JVM discovers and invokes it via reflection during deserialization.

transient vs. Other Exclusion Strategies

You have a few ways to exclude a field from serialization:

ApproachMechanismBest for
transient keywordBuilt-in JVM supportSimple field exclusion
Custom writeObject / readObjectFull control over the streamComplex partial serialization
Externalizable interfaceYou write everything manuallyMaximum control / performance
serialPersistentFields arrayDeclare exactly which fields serializeLegacy / library code

For most projects, transient covers 95% of needs. Reach for Externalizable only when you need fine-grained binary control or maximum performance.

Under the Hood

When ObjectOutputStream.writeObject(obj) runs, it reflects over the object’s class hierarchy to build a list of all non-static, non-transient fields. Each field’s value is then written to the stream in a defined binary format. The transient modifier is exposed through java.lang.reflect.Field.getModifiers() — the serialization engine checks Modifier.isTransient(field.getModifiers()) and simply skips any field that returns true.

During deserialization, ObjectInputStream allocates a new instance without calling any constructor (it uses sun.reflect.ReflectionFactory or JVM-internal allocation). It then sets only the non-transient fields. Because no constructor runs, transient fields receive their Java language default values (null, 0, false, etc.) — which is exactly why the readObject hook exists.

Tip: Because no constructor runs during deserialization, any validation logic in your constructor is bypassed. For security-sensitive classes, consider implementing readObject to validate invariants after restoration.

Interaction with serialVersionUID

transient fields do not affect serialVersionUID compatibility the same way structural changes do. Adding or removing a transient field does not break deserialization of previously serialized objects — the old stream simply doesn’t contain that field, and it defaults to null / zero. This makes transient a safe way to evolve a serializable class without bumping the UID.

Note: Always declare private static final long serialVersionUID explicitly in every Serializable class. Without it, the JVM auto-generates one from the class structure, and any small code change can break deserialization of saved data.

  • Serialization — the full picture of Java object serialization and deserialization
  • Object StreamsObjectInputStream and ObjectOutputStream in depth
  • final Keyword — another field-level modifier that pairs frequently with transient
  • Reflection API — how the JVM inspects fields and modifiers at runtime
  • Custom Exceptions — custom exception classes are Serializable and often contain transient fields
  • volatile Keyword — another field modifier (for thread visibility) commonly confused with transient
Last updated June 13, 2026
Was this helpful?