Code Trace: Static Block vs Instance Block vs Constructor Execution Order
Interview Question: "Given the following Java code involving inheritance, static initializer blocks, instance initializer blocks, and constructors, what is the exact output in order? Explain why static blocks run when they do, and how the JVM compiler synthesizes
<clinit>and<init>methods."class Parent {static {System.out.println("1. Parent Static Block");}{System.out.println("3. Parent Instance Block");}Parent() {System.out.println("4. Parent Constructor");}}class Child extends Parent {static {System.out.println("2. Child Static Block");}{System.out.println("5. Child Instance Block");}Child() {System.out.println("6. Child Constructor");}public static void main(String[] args) {System.out.println("--- Main Starts ---");new Child();System.out.println("--- Second Instantiation ---");new Child();}}
Most candidates know "static blocks run before constructors." The actual test here is whether you know why — the JVM runs two genuinely separate phases: loading the class (<clinit>) and creating an instance of it (<init>), and the rules are different for each.
Exact Output
1. Parent Static Block
2. Child Static Block
--- Main Starts ---
3. Parent Instance Block
4. Parent Constructor
5. Child Instance Block
6. Child Constructor
--- Second Instantiation ---
3. Parent Instance Block
4. Parent Constructor
5. Child Instance Block
6. Child Constructor
Step-by-Step Execution Trace
Parent static blockChild static blockprints '--- Main Starts ---'Parent instance blockParent constructorChild instance blockChild constructorrepeats steps 3–6 onlyDetailed Breakdown of Critical Inflection Points:
-
Why do static blocks run before
main starts? Before the JVM can invoke themainmethod ofChild, it must load, link, and initializeChild.class. SinceChild extends Parent, the JVM specification mandates that all superclasses must be fully initialized before a subclass can be initialized. Thus,Parent's static initializers execute first, followed immediately byChild's static initializers, all before line 1 ofmain()begins. -
The Inheritance Trap: Child Static vs. Parent Instance: Candidates often guess that
Parent Instance Blockruns beforeChild Static Block. This is false. All static initializers across the entire class hierarchy execute during class loading, which finishes entirely before any object instantiation begins. -
Why do static blocks not run on the second
new Child()? Static blocks are compiled into a synthetic static method called<clinit>(Class Initializer). The JVM ClassLoader executes<clinit>exactly once when the class is first loaded into memory (JVM Metaspace). Subsequent object instantiations skip class loading entirely. -
Instance Blocks execute inside the Constructor (
<init>): Java does not have an independent bytecode concept for "instance initializer blocks." The Java compiler (javac) physically extracts all instance variable assignments and{ ... }instance initializer blocks and inlines them directly into every constructor (<init>), positioned immediately after thesuper(...)call and before the constructor's own body.
What javac Actually Generates: <clinit> vs <init>
Understanding how javac transforms source code into bytecode demystifies the execution order:
| Source Construct | Synthesized JVM Method | Invocation Trigger | Execution Frequency |
|---|---|---|---|
static { ... } and static T x = val; | <clinit> (Class Initializer) | First active use of class (e.g. main(), static access, new) | Exactly once per ClassLoader lifecycle |
{ ... } and instance T y = val; | Inlined inside <init> (Constructor) | After super(...) returns, before constructor body | Once per new instantiation |
Class constructor Class() { ... } | <init> (Instance Initializer) | Explicit new Class() call | Once per new instantiation |
// Simplified Java Bytecode representation for Child.<init>
Method Child.<init>() {
aload_0
invokespecial Parent.<init>() // 1. Invoke Superclass Constructor
// Inlined Child Instance Initializer Block:
getstatic java/lang/System.out
ldc "5. Child Instance Block"
invokevirtual PrintStream.println()
// Child Constructor Body:
getstatic java/lang/System.out
ldc "6. Child Constructor"
invokevirtual PrintStream.println()
return
}
The Follow-Up Trap: What If a Static Block Throws?
Interviewers frequently follow up with: "What happens if a static block throws an uncaught exception?"
If an uncaught exception (such as NullPointerException or ArithmeticException) occurs inside a static block:
- The JVM wraps it in an
ExceptionInInitializerErrorand aborts class initialization. - The class enters an erroneous state in the ClassLoader registry.
- If code subsequently attempts to catch the exception and instantiate the class again, the JVM will not re-run the static block. Instead, it immediately throws a
NoClassDefFoundError:
public class BrokenClass {
static {
int x = 10 / 0; // Throws ArithmeticException wrapped in ExceptionInInitializerError
}
}
// In client code:
try {
new BrokenClass();
} catch (Throwable t) {
System.out.println(t); // java.lang.ExceptionInInitializerError
}
// Subsequent attempt:
new BrokenClass(); // CRASHES IMMEDIATELY with java.lang.NoClassDefFoundError!
Java: Runnable Verification
This standalone Java program verifies the entire execution lifecycle:
/**
* Standalone verification for Java class initialization ordering.
* Run with: javac ExecutionOrderDemo.java && java ExecutionOrderDemo
*/
public class ExecutionOrderDemo {
static class Parent {
static {
System.out.println("1. Parent Static Block");
}
{
System.out.println("3. Parent Instance Block");
}
Parent() {
System.out.println("4. Parent Constructor");
}
}
static class Child extends Parent {
static {
System.out.println("2. Child Static Block");
}
{
System.out.println("5. Child Instance Block");
}
Child() {
System.out.println("6. Child Constructor");
}
}
public static void main(String[] args) {
System.out.println("--- Main Starts ---");
System.out.println("First instantiation:");
new Child();
System.out.println("\nSecond instantiation:");
new Child();
}
}
Say this in the interview
"Two separate phases: class loading (<clinit>) runs once, superclass before subclass, entirely before main() starts. Instance creation (<init>) runs on every new — it calls super() first, which triggers the parent's instance blocks then its constructor, before coming back to run the child's instance blocks and constructor. Static blocks never run again after the first load; only <init> repeats on later instantiations."