final field semantics
The Java Memory Model's special promise for final fields: the freeze at the end of the constructor, why String and records are safe to share, how letting this escape voids it, why final is shallow, and how to share reloadable config with immutable snapshots.
On this page
- Picture it first: a whiteboard vs a printed certificate
- Under the hood: what the freeze promises
- Why String is safe to share
- The big caveat: this must not escape
- final freezes the arrow, not the box
- Sharing a config object across threads
- Real world
- Advanced: how the freeze is implemented, and where it leaks
- How to say it in the interview
🔏 What are final field semantics?
The Java Memory Model gives final fields one special promise (JLS 17.5): if another thread sees a reference to an object after its constructor finished, it sees the values the constructor put in that object's final fields, with no locks and no volatile. Think of a certificate that gets printed and stamped before anyone is allowed to read it. Plain fields get no such promise: a thread that grabs the object through a race can see 0 or null where you wrote 5432.
For this lesson, we'll learn what that promise covers, the "freeze" that happens at the closing brace of a constructor, why String is safe to toss between threads, and the one mistake that cancels the whole deal: letting this escape while the constructor is still running. Interviewers love this topic because final looks like a compile-time keyword about reassignment, and then they ask "so is a final List thread-safe?" or "why is an immutable object safe even when published through a data race?". Those need a real model, not a slogan.
What you’ll learn
- State the final-field guarantee of JLS 17.5: a thread that sees a reference after the constructor finished sees the constructor's
finalvalues, even through a data race - Explain the freeze at the end of the constructor and the reachability rule for objects and arrays referenced by
finalfields - Explain why **
Stringand records are safe to share** without synchronization, and why plain fields in the same object are not - Spot the ways **
thisescapes a constructor** (listeners,start(), statics, inner classes) and fix them with a static factory - Separate a final reference from an immutable object, and use
List.copyOfin a record's compact constructor - Apply the JCIP 3.4 immutability recipe and say why
finaldoes not publish a new reference - Share reloadable config with **immutable snapshots behind an
AtomicReference** - Describe how HotSpot implements the freeze on x86 vs ARM, and the JDK 26 restrictions on mutating finals (JEP 500)
Premium lesson
Keep reading with the Java Pass
The rest of this lesson is part of Java One-Stop Interview Prep: in-depth explanations, diagrams, model answers and how to say them in the interview.
- Picture it first: a whiteboard vs a printed certificate
- Under the hood: what the freeze promises
- Why String is safe to share
- The big caveat: this must not escape
- final freezes the arrow, not the box
- Sharing a config object across threads
- Real world
- Advanced: how the freeze is implemented, and where it leaks
- How to say it in the interview
From ₹299 · one-time payment · No auto-renewal
Further reading (13)
- Java Language Specification (Java SE 21) Chapter 17: Threads and Locks: §17.5 final field semantics, §17.5.1 the freeze action, §17.5.3 subsequent modification of final fields (reflection, deserialization, constant inlining)
- JSR 133 (Java Memory Model) FAQ, Manson and Goetz: how final fields work under the new JMM, the String substring offset example, why this must not escape
- jcstress sample BasicJMM_08_Finals: recorded x86_64 and AArch64 runs: partially constructed objects with plain fields, only complete or not-yet with finals
- Aleksey Shipilev: All Fields Are Final: how HotSpot implements the constructor-exit barrier and its cost on x86 vs weaker architectures
- Aleksey Shipilev: Java Memory Model Pragmatics: final field semantics in the context of happens-before and safe publication
- Aleksey Shipilev: Close Encounters of The Java Memory Model Kind: common misconceptions, including assuming finals publish the reference
- JEP 500: Prepare to Make Final Mean Final: JDK 26 warns on reflective mutation of final fields; --enable-final-field-mutation and --illegal-final-field-mutation; records and hidden classes already refuse
- JEP draft: Integrity by Default: why the platform is closing back doors like deep reflection and Unsafe
- JEP 498: Warn upon Use of Memory-Access Methods in sun.misc.Unsafe: JDK 24 warning on first use of Unsafe memory-access methods
- JEP 395: Records: record components are private final fields; compact constructors for validation and defensive copies
- Field.set (Java SE 21 API): rules for setting final fields reflectively; IllegalAccessException for record and hidden class fields
- AtomicReference (Java SE 21 API): get/set with volatile semantics, updateAndGet for snapshot swaps
- Java Concurrency in Practice (Goetz et al., 2006): §3.2 publication and escape, §3.4 immutability recipe, §3.5 safe publication and initialization safety