Study guide

14.2 Object Serialization, `Serializable`, `transient`, and `serialVersionUID`

Oracle Java SE 17 Developer Certified Professional 1Z0-829

Track your progress as you read

Finished sections get a check mark.

1 Introduction: The 1Z0-829 Exam

About the Java SE 17 Developer (1Z0-829) Exam

2 Chapter 1: Java Fundamentals & Language Basics

1.1 Primitive Types, Literals, Wrapper Classes, and Type Promotion/Casting 1.2 Local Variable Type Inference (`var`) Rules and Scoping 1.3 Operators, Precedence, Evaluation Order, and Short-Circuit Evaluation 1.4 Text Blocks, String Immutability, and StringBuilder APIs

3 Chapter 2: Program Flow & Decision Constructs

2.1 Conditional Branching with if/else and the Ternary Operator 2.2 Switch Statements and Switch Expressions 2.3 Iteration Constructs, Loop Control, and Branching Labels

4 Chapter 3: Object-Oriented Design & Class Mechanics

3.1 Class Declarations, Field/Method Signatures, and Access Modifiers 3.2 Constructors, Overloading, Initialization Order, and Garbage Collection 3.3 Inheritance, Method Overriding, Polymorphism, and `super`/`this` 3.4 Abstract Classes, Static Members, and Object Methods (`equals`, `hashCode`, `toString`)

5 Chapter 4: Advanced OOP: Interfaces, Records & Sealed Types

4.1 Interface Architecture: Default, Static, and Private Methods 4.2 Enumerations: Constants, Constructors, Methods, and Custom Bodies 4.3 Records: Canonical/Compact Constructors, Components, and Immutability 4.4 Sealed Classes and Interfaces: `permits`, Hierarchy Constraints, and Modifiers

6 Chapter 5: Pattern Matching & Nested Types

5.1 Pattern Matching for `instanceof` and Flow Scoping Mechanics 5.2 Nested Types: Member Inner, Static Nested, Local, and Anonymous Classes 5.3 Defensive Programming, Immutability Patterns, and Validation

7 Chapter 6: Generics, Collections & Arrays

6.1 Generic Classes, Interfaces, Methods, and Type Erasure 6.2 Bounded Type Parameters and Wildcards (PECS Rule) 6.3 Collections Framework: List, Set, Queue, and Deque APIs 6.4 The Map Interface, Sorting, Comparable, and Comparator 6.5 Arrays: Declaration, Initialization, Multidimensional Arrays, and the Arrays Utility Class

8 Chapter 7: Functional Programming & Lambda Expressions

7.1 Functional Interfaces and Lambda Syntax Rules 7.2 Standard Functional Interfaces in `java.util.function` 7.3 Method References: Static, Bound, Unbound, and Constructor References

9 Chapter 8: The Stream API

8.1 Stream Lifecycle: Creation, Pipeline Architecture, and Lazy Evaluation 8.2 Intermediate Stream Operations: Filtering, Mapping, Sorting, and Slicing 8.3 Terminal Stream Operations: Reduction, Matching, Finding, and Collecting 8.4 Primitive Streams (`IntStream`, `LongStream`, `DoubleStream`) and Numeric Statistics

10 Chapter 9: Advanced Streams & Collectors

9.1 The Optional Class: Safe Null Handling, Transformation, and Chaining 9.2 The Collectors API: Downstream Reductions, Grouping, and Partitioning 9.3 Parallel Streams: Fork/Join Pool, Spliterators, and Concurrency Pitfalls

11 Chapter 10: Exception Handling

10.1 The Java Exception Hierarchy: Checked vs. Unchecked Exceptions and Errors 10.2 Exception Constructs: `try-catch-finally`, Multi-Catch, and Rethrowing 10.3 Try-With-Resources, `AutoCloseable`, Suppressed Exceptions, and Custom Exceptions

12 Chapter 11: Date/Time API & Localization

11.1 Date and Time Core Types: `LocalDate`, `LocalTime`, `LocalDateTime` 11.2 Timestamps, Intervals, and Durations: `Instant`, `Period`, and `Duration` 11.3 Time Zones, Daylight Saving Time (DST) Offsets, and `ZonedDateTime` 11.4 Internationalization and Localization: `Locale`, `ResourceBundle`, and Formatters 11.5 Formatting and Parsing Messages, Numbers, Percentages, and Localized Dates

13 Chapter 12: Java Platform Module System (JPMS)

12.1 Module Architecture, `module-info.java`, and Module Paths 12.2 Module Directives: `requires`, `requires transitive`, `exports`, and Qualified Exports 12.3 Reflection, Deep Encapsulation, `opens`, and Open Modules 12.4 Services in JPMS (`uses`, `provides...with`), Migration Paths, and Tooling (`jlink`, `jdeps`) 12.5 Compiling and Packaging Code: Non-Modular JARs, Modular JARs, and Migration Strategies

14 Chapter 13: Concurrency & Multithreading

13.1 Thread Fundamentals, `Runnable`, `Callable`, and Thread Lifecycle 13.2 Memory Visibility, `volatile`, Intrinsic Locks, and Synchronization 13.3 The Concurrency Framework: `ExecutorService`, Thread Pools, and `Future` 13.4 Concurrent Collections, Atomic Variables, Explicit Locks, and `CompletableFuture`

15 Chapter 14: Java I/O and NIO.2

14.1 Classical I/O: Byte and Character Streams, Readers, Writers, and Buffering 14.2 Object Serialization, `Serializable`, `transient`, and `serialVersionUID` 14.3 NIO.2 Architecture: `Path`, `Paths`, and Basic `Files` Operations 14.4 Advanced NIO.2: Directory Walking, Streams (`Files.lines`, `walk`), and `WatchService`

16 Chapter 15: Accessing Databases with JDBC

15.1 JDBC Architecture, Drivers, `DriverManager`, and `DataSource` Connections 15.2 SQL Execution: `Statement`, `PreparedStatement`, and `CallableStatement` 15.3 `ResultSet` Navigation, Data Types, and Transaction Management (Commit, Rollback, Savepoints)

17 Chapter 16: Annotations & the Java Logging API

16.1 Standard Annotations: @Override, @FunctionalInterface, @Deprecated, @SuppressWarnings, and @SafeVarargs 16.2 Java Logging API Basics: Logger, Levels, Handlers, and Configuration

14.2 Object Serialization, `Serializable`, `transient`, and `serialVersionUID`

Key Takeaways Explain This Topic AI

Object serialization converts live object graphs into a portable byte stream via ObjectOutputStream, while deserialization reconstitutes them via ObjectInputStream.

Classes must implement the java.io.Serializable marker interface; all reachable non-transient instance references in the object graph must also be Serializable, or NotSerializableException is thrown at runtime.

transient instance fields and static fields are omitted from serialization; upon deserialization, transient fields default to null, 0, or false, and constructors of the serializable class do not execute.

If a superclass does not implement Serializable, it must provide an accessible no-argument constructor that executes during deserialization; failing this requirement causes InvalidClassException.

Java records bypass custom writeObject and readObject methods, always invoking their canonical constructor during deserialization to enforce component invariants.

Last updated: October 2026

14.2 Object Serialization, Serializable , transient , and serialVersionUID

Object serialization is the mechanism by which Java converts an active object graph in memory into an ordered byte stream suitable for persistent storage on disk or transmission across a network. Deserialization is the reverse process: reconstructing the live object graph from the serialized byte stream. The objective "Serialize and de-serialize Java objects" covers serialization mechanics, including the Serializable marker interface, field eligibility ( transient vs. static ), superclass constructor execution, versioning via serialVersionUID , custom serialization hooks, and the specialized deserialization rules governing Java records.

Serialization Mechanics & The Serializable Interface

To serialize and deserialize objects, Java provides two high-level decorator streams in java.io :

ObjectOutputStream : Wraps an underlying OutputStream and writes objects using void writeObject(Object obj) throws IOException .

ObjectInputStream : Wraps an underlying InputStream and reconstitutes objects using Object readObject() throws IOException, ClassNotFoundException .

The Serializable Marker Interface

For an object to be serializable, its class must implement java.io.Serializable . This is a marker interface (it contains no fields and no methods). It acts as a metadata flag to the JVM indicating that the class author authorizes its internal state to be encoded into bytes. import java.io.Serializable;

  • public class Employee implements Serializable {
  • private String name;
  • private double salary;
  • public Employee(String name, double salary) {
  • this.name = name;
  • this.salary = salary;
  • }
  • }
  • Deep Object Graph Traversal & NotSerializableException

When writeObject() is invoked, Java serializes not just the root object, but recursively traverses its entire reference graph. Every object reachable through non-transient instance fields must also implement Serializable .

If any reachable object in the object graph does not implement Serializable , the serialization process halts immediately and throws java.io.NotSerializableException at runtime, identifying the unqualified class. public class Department implements Serializable {

  • private String deptName;
  • private Manager manager; // If Manager does NOT implement Serializable,

// serializing Department throws NotSerializableException!

  • }
  • What Gets Serialized: instance , static , and transient Fields

Understanding which fields are included in the serialized stream is a critical exam topic:

Instance Fields : Included by default. The values of all non-transient instance fields are written to the stream.

static Fields : Never serialized . Static fields belong to the class definition loaded in the JVM's ClassLoader , not to any individual instance. When an object is deserialized, static fields reflect whatever value is currently loaded in the receiving JVM.

transient Fields : Omitted from serialization . The transient keyword marks fields containing sensitive data (such as passwords, credit card numbers, or cryptographic keys) or transient runtime state (such as file handles, thread references, or cached calculations) that should not be persisted.

Deserialization Field Initialization & Constructor Execution

When an object is deserialized via readObject() :

Constructors of the Serializable class DO NOT run! The JVM allocates memory directly and restores instance fields from the serialized byte stream.

Instance initializers and field default assignments DO NOT run for the serializable class.

transient fields are initialized to their default type values :

Object references default to null .

Numeric primitives ( byte , short , int , long , float , double ) default to 0 or 0.0 .

boolean primitives default to false .

char primitives default to \u0000 . import java.io.*;

  • public class Account implements Serializable {
  • private static int globalCounter = 100; // NOT serialized
  • private String accountNumber; // Serialized
  • private transient String secretPin; // OMITTED: defaults to null on deserialization
  • private transient int failedAttempts; // OMITTED: defaults to 0 on deserialization
  • public Account(String acc, String pin) {
  • this.accountNumber = acc;
  • this.secretPin = pin;
  • this.failedAttempts = 3;
  • }
  • }
  • Class Inheritance in Serialization

Inheritance introduces specific contracts depending on which classes in the hierarchy implement Serializable :

Subclasses of Serializable Superclasses

If a class extends a superclass that implements Serializable , the subclass is automatically serializable by inheritance, even if the subclass does not explicitly declare implements Serializable .

Non-Serializable Superclasses

When a serializable class extends a superclass that does NOT implement Serializable :

The non-serializable superclass state is not automatically serialized.

Mandatory Requirement : The non-serializable superclass must have an accessible no-argument constructor (public, protected, or package-private if in the same package).

Execution during Deserialization : During deserialization, the JVM invokes the non-serializable superclass's no-argument constructor to initialize its inherited fields. The serializable subclass constructor still does not run.

Failure Condition : If the non-serializable superclass lacks an accessible no-argument constructor, readObject() immediately throws java.io.InvalidClassException at runtime! // Non-serializable superclass

  • class Vehicle {
  • protected String registrationNumber;

// Mandatory: Must have an accessible no-arg constructor!

  • public Vehicle() {
  • this.registrationNumber = "UNREGISTERED";
  • }
  • public Vehicle(String reg) {
  • this.registrationNumber = reg;
  • }
  • }
  • // Serializable subclass
  • class Car extends Vehicle implements Serializable {
  • private String model;
  • public Car(String reg, String model) {
  • super(reg);
  • this.model = model;
  • }
  • }

When Car is deserialized:

Vehicle() (the no-arg constructor of Vehicle ) executes, setting registrationNumber to "UNREGISTERED" .

Car 's instance field model is restored from the stream to its original serialized value.

The parameterized Car(String, String) constructor is bypassed entirely.

Version Compatibility with serialVersionUID

Java serialization assigns a version number called serialVersionUID to each serializable class. This identifier verifies that the class definition loaded in the receiving JVM is compatible with the byte stream being deserialized. private static final long serialVersionUID = 1L;

What Happens if serialVersionUID is Omitted?

If a class does not explicitly declare serialVersionUID , the serialization runtime computes a default hash value at run time from class details (class name, modifiers, implemented interfaces, field names, and method signatures).

However, relying on automatic calculation is dangerous:

Any trivial change to the class (adding an internal helper method, reordering fields, or recompiling with a different compiler) alters the generated hash.

When deserializing previously stored instances, the JVM compares the stream's UID with the current class's UID. If they mismatch, deserialization fails with java.io.InvalidClassException .

Explicitly declaring private static final long serialVersionUID maintains backward compatibility across class revisions.

Custom Serialization Protocols

Classes can customize their serialization process by defining special private hook methods: private void writeObject(java.io.ObjectOutputStream out) throws java.io.IOException {

  • out.defaultWriteObject(); // Serializes standard non-transient fields
  • out.writeInt(computeChecksum()); // Writes custom extra data
  • }
  • private void readObject(java.io.ObjectInputStream in)
  • throws java.io.IOException, ClassNotFoundException {
  • in.defaultReadObject(); // Reconstitutes standard non-transient fields
  • this.checksum = in.readInt(); // Reads custom extra data
  • this.calculatedHash = recalculate(); // Re-initializes transient fields
  • }

Rules for custom hooks:

Both methods must be declared private void .

out.defaultWriteObject() and in.defaultReadObject() must be called first to handle standard field processing.

writeReplace() and readResolve() : Enable object substitution. readResolve() is commonly used to protect the Singleton Pattern , ensuring that deserialization returns the existing singleton instance rather than creating a duplicate object. public class ConfigurationManager implements Serializable {

  • private static final ConfigurationManager INSTANCE = new ConfigurationManager();
  • private ConfigurationManager() {}
  • public static ConfigurationManager getInstance() { return INSTANCE; }
  • // Guarantees singleton identity after deserialization
  • private Object readResolve() throws ObjectStreamException {
  • return INSTANCE;
  • }
  • }
  • Serialization of Java Records (Java 16+)

Java records ( record ) represent immutable data carriers. Record serialization differs sharply from regular class serialization:

Dimension Standard Class Serialization Record Serialization ( record )

Constructor Execution Bypassed : Fields are restored directly by JVM reflection; constructors do NOT run Mandatory : Deserialization always invokes the canonical constructor

Invariant Enforcement Compact/custom constructor validation logic is skipped Compact/canonical constructor validations always execute

Custom Serialization Methods Respects writeObject() , readObject() , readObjectNoData() Completely ignores writeObject , readObject , and readObjectNoData

serialPersistentFields Configures custom field lists Ignored; components define serialized fields directly

Replacement Hooks Supports writeReplace() and readResolve() Supports writeReplace() and readResolve() import java.io.Serializable;

  • public record UserProfile(String username, int age) implements Serializable {
  • public UserProfile {
  • if (age < 0) {
  • throw new IllegalArgumentException("Age cannot be negative: " + age);
  • }
  • }
  • }

If a malicious actor crafts a corrupted byte stream with age = -5 :

A standard class would deserialize the object with -5 because constructors are bypassed.

The record deserialization process passes the stream values into the canonical constructor, triggering IllegalArgumentException and rejecting the invalid state!