How can a final instance field be initialized, and what guarantees does the compiler enforce for blank final fields?
answer
- final field set at declaration, init block, or constructor
- blank final = assigned later, once, on all paths
- definite assignment: at least once AND at most once per constructor
- this(...) chaining: only the delegate assigns
- JMM: final fields safely published if this doesn't escape
basics
~20 sA final instance field can be set at its declaration or inside every constructor, but exactly once. The compiler checks that by the time any constructor finishes, every final field has been assigned on all paths, and never twice. This is great for required, unchanging fields.
solid answer
~50 sA final instance field may be initialized in its declaration, in an instance initializer block, or in a constructor body, but it must be definitely assigned exactly once before the constructor completes, on every code path. A field left unset at declaration is a blank final, commonly used to assign constructor arguments to immutable fields. The compiler enforces: assigned at least once on all paths (no uninitialized final), and at most once (no reassignment). Each constructor must independently satisfy this; if constructors chain with this(...), the chained constructor must do the assignment. Beyond compile-time guarantees, the Java Memory Model gives final fields a safe-publication property: once an object is constructed without letting this escape, other threads that obtain the reference are guaranteed to see the correctly initialized final fields, even without synchronization. This underpins safe immutable objects.
code
java · 11 linespublic final class Range {
private final int lo;
private final int hi; // blank finals
public Range(int lo, int hi) {
if (lo > hi) throw new IllegalArgumentException();
this.lo = lo; // each assigned exactly once on every path
this.hi = hi;
}
// No setters: lo/hi never change -> immutable, safely publishable
}go deeper
Knows a final field must be given a value (at declaration or in the constructor) and cannot change after.
Explains blank finals and that every constructor must assign each final field exactly once on all paths.
Covers constructor chaining rules and the JMM safe-publication guarantee for final fields in immutable objects.
Designs thread-safe immutable types relying on final-field publication, reasons about this-escape hazards and how reordering affects non-final fields.
## final on an instance field An **instance field** is a variable that belongs to each object. Marking it `final` means it must be assigned **exactly once** and then never changed. Unlike a local, a `final` instance field has three legal places to be assigned: 1. **At the declaration:** ```java private final int max = 100; ``` 2. **In an instance initializer block** (a `{ ... }` block in the class body): ```java private final int max; { max = 100; } ``` 3. **In a constructor:** ```java private final int max; MyClass(int m) { this.max = m; } ``` A field declared `final` but not assigned at its declaration is a **blank final** — extremely common for binding constructor arguments into immutable objects. ## What the compiler guarantees (definite assignment) For every constructor, the compiler proves two things via **definite assignment analysis**: - **At least once:** every `final` field is assigned on **every** code path before the constructor returns. You cannot leave a final field unset. - **At most once:** no `final` field is assigned a second time on any path. ```java final int x; MyClass(boolean b) { if (b) { x = 1; } // ERROR if no else: x might be unassigned } ``` This must hold **independently for each constructor**. If you have multiple constructors, each must fully initialize the final fields. ## Constructor chaining with this(...) If one constructor delegates to another with `this(...)`, the **delegated-to** constructor is responsible for the assignment; the delegating one must **not** also assign (that would be a second assignment): ```java final int x; MyClass() { this(0); } // delegates MyClass(int v) { this.x = v; } // does the single assignment ``` ## Static final fields The analogous rule applies to `static final` fields, which may be assigned at declaration or in a **static initializer block**, exactly once across all static-init paths. ## The Java Memory Model safe-publication guarantee There is a deeper, runtime guarantee for `final` fields. The **Java Memory Model (JMM)** promises that if an object is properly constructed — meaning the `this` reference does **not escape** during construction — then any thread that later obtains a reference to the object is guaranteed to see the **correctly initialized values** of its `final` fields, **without any synchronization**. This is the foundation of **safe immutable objects**: a class with only `final` fields, no escaping `this`, and no mutators can be freely shared across threads. Non-final fields enjoy no such guarantee — another thread might see their default (zero/null) values due to reordering. ```java public final class Money { private final long cents; // final -> safely published public Money(long cents) { this.cents = cents; } // this never escapes } ``` ## Why this matters - It lets you build **immutable, thread-safe** value types with no locks. - It documents required, never-changing dependencies (constructor-injected collaborators). - The compiler catches forgotten or duplicated initialization at build time.
- What happens if a constructor leaves a final field unassigned on some path?The code does not compile. Definite-assignment analysis requires every final field to be assigned on all paths before the constructor returns; an uninitialized final on any path is a compile error.
- What does the Java Memory Model guarantee for final fields?If the object is constructed without letting this escape, any thread that later gets the reference is guaranteed to observe the fully initialized final fields without synchronization. Non-final fields get no such guarantee.
A blank final field is like a contract clause left blank to be filled in at signing (the constructor): it must be completed before the contract takes effect, and once signed it cannot be edited.
saying these in an interview costs you the question
- Thinking a blank final can be left unassigned with a default value.
- Assigning the same final field in both a chaining and a chained constructor.
- Assuming non-final fields get the same safe-publication guarantee.
- Believing a final field can only be set on the declaration line.