Java Class Initialization
Class initialization is the JVM phase that executes a class's static initialization logic when the class must be initialized. It prepares static state before the class is used in ways that require initialization.
Class initialization is distinct from class loading and linking. After a class is loaded and its relevant preparation steps are complete, the JVM may initialize it by executing its static initialization method, commonly represented as `<clinit>` in JVM terminology.
Initialization processes static field initializers and `static` blocks in the order they appear in the class's source-level initialization sequence. Static fields first receive default values during class preparation; their explicit initializers run later during initialization.
A class is typically initialized when the JVM encounters an active-use trigger, such as executing a static method, accessing a non-constant static field, or creating an instance. Reading a compile-time constant may not trigger initialization because its value can be embedded directly by the compiler.
Class initialization is synchronized by the JVM so that initialization of a class occurs in a controlled manner when multiple threads encounter the class concurrently. If initialization fails with an exception, the JVM records the failure for that class initialization attempt and subsequent active use can result in `NoClassDefFoundError` rather than simply retrying the original initialization normally.
Initialization also respects superclass ordering: before a class is initialized, its relevant superclass must already have been initialized. Interfaces have different initialization behavior; initializing a class does not generally initialize all of its implemented interfaces.
Key points
- `<clinit>` contains compiler-generated class initialization logic for static initializers.
- Static fields receive default values before explicit static initialization runs.
- Active use can trigger initialization; merely loading a class does not necessarily do so.
- Static initialization follows the relevant textual order of field initializers and `static` blocks.
- Superclass initialization precedes initialization of a class.
Interview questions
What is the difference between class loading, linking, and initialization?
Class loading brings the class definition into the JVM and creates its runtime representation. Linking includes verification, preparation, and resolution as applicable. Initialization then executes the class's static initialization logic, represented by `<clinit>` when such logic exists. These phases are related but should not be treated as one operation.
When does the JVM initialize a class?
Initialization occurs when the JVM determines that a class must be initialized, commonly because of active use. Examples include invoking a static method, accessing a non-constant static field, or creating an instance. A compile-time constant can often be accessed without initializing its declaring class. The exact trigger matters when debugging static initialization behavior.
In what order do static initializers execute?
Static initialization follows the initialization rules for the class hierarchy and then the class's own initialization sequence. Within a class, explicit static field initializers and static blocks execute in their source order. Before a class is initialized, its superclass is initialized first. This ordering is important when one static field depends on another.
What happens if static initialization throws an exception?
If class initialization completes abruptly because an exception escapes, initialization is considered to have failed. The JVM records the class as being in an erroneous initialization state. Later active use generally results in `NoClassDefFoundError`, with the earlier initialization failure typically available through the cause chain. This is why failures in static initialization can appear far from the original code that caused the exception.
How does the JVM handle concurrent initialization of the same class?
Class initialization is coordinated by the JVM so that initialization of a particular class is performed safely when multiple threads encounter it. A thread that requires initialization may perform the work while other threads wait according to the JVM's initialization protocol. Once initialization completes successfully, subsequent uses observe the initialized state. This coordination is one reason static initialization can be used to establish safely published class-level state when designed correctly.