Skip to main content

Documentation Index

Fetch the complete documentation index at: https://docs.syntblaze.com/llms.txt

Use this file to discover all available pages before exploring further.

A final field in Java is a class or instance variable that can be assigned a value exactly once. Once initialized, its value (for primitives) or memory reference (for objects) becomes strictly immutable and cannot be reassigned during the lifetime of the object or class.

Initialization Rules

The Java compiler enforces strict definite assignment rules for final fields. A final field must be initialized before the constructor (or static initialization, if static) completes. A final field that is not initialized at the point of declaration is called a blank final field.
  1. Instance Final Fields: Must be initialized either at the point of declaration, within an instance initializer block, or within every constructor of the class.
  2. Static Final Fields: Must be initialized either at the point of declaration or within a static initializer block.
public class FinalFieldMechanics {
    // 1. Direct initialization
    private final int directValue = 10;

    // 2. Blank instance final field
    private final String blankInstanceValue;

    // 3. Blank static final field
    private static final double STATIC_VALUE;

    // Static initializer block for static final fields
    static {
        STATIC_VALUE = 3.14159;
    }

    // Constructor initialization for blank instance final fields
    public FinalFieldMechanics(String value) {
        // The compiler verifies this is assigned exactly once per constructor path
        this.blankInstanceValue = value; 
    }
}

Reference Immutability vs. Object State Immutability

Applying the final keyword to a reference type guarantees reference immutability, not object state immutability. The field cannot be reassigned to point to a different memory location, but the internal state of the referenced object can still be mutated if the object itself is mutable.
import java.util.List;
import java.util.ArrayList;

public class ReferenceSemantics {
    private final List<String> items = new ArrayList<>();

    public void modifyState() {
        // Valid: Mutating the internal state of the referenced object
        items.add("Element"); 
        
        // Compilation Error: Cannot reassign a final reference
        // items = new ArrayList<>(); 
    }
}

Java Memory Model (JMM) Semantics

Under the Java Memory Model (JSR-133), final fields provide a specific thread-safety guarantee known as initialization safety. When an object is constructed, the thread performing the initialization writes to the final fields. The JMM guarantees that once the constructor completes, any other thread that acquires a reference to that object will immediately see the correctly initialized values of its final fields, without requiring explicit synchronization (like volatile or synchronized). This guarantee is subject to the no-escape rule: the this reference of the object being constructed must not escape the constructor. If this is published to another thread before the constructor completes, that thread may observe the final fields in their default (uninitialized) states.
public class JmmSemantics {
    private final int x;
    private final int y;

    public JmmSemantics(int x, int y) {
        this.x = x;
        this.y = y;
        
        // DANGER: 'this' escapes before constructor completes.
        // Other threads might see x and y as 0.
        // GlobalState.publish(this); 
    }
}

Reflection and Final Fields

Historically, final fields could be modified at runtime using the Java Reflection API via Field.setAccessible(true). However, modern Java enforces strict security boundaries that restrict this capability to prevent runtime instability:
  • Since Java 12: Modifying static final fields via reflection is strictly prohibited and throws an IllegalAccessException.
  • Since Java 15: Modifying final fields inside Record classes is also blocked and throws an IllegalAccessException.
For standard instance fields where reflection is still permitted, modifying a final field violates JMM guarantees. The JVM aggressively inlines final fields (especially constants), meaning reflective modifications may not be visible to code that has already cached the original value, leading to unpredictable behavior.
Master Java with Deep Grasping Methodology!Learn More