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();}}
This question is a standard FAANG/Tier-1 screening problem. While entry-level candidates memorize that static blocks execute before constructors, staff-level interviewers evaluate whether you understand the JVM's distinct Class Loading Phase (<clinit>) versus the Instance Creation Phase (<init>) across an inheritance hierarchy.
1. 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
2. Step-by-Step Top-Down Execution Trace
+-------------------------------------------------------------------------------+
| PHASE 1: CLASS LOADING & VERIFICATION (<clinit>) |
| Trigger: JVM invokes Child.main() -> Loads Parent.class, then Child.class |
| 1. Parent Static Initializers execute -> "1. Parent Static Block" |
| 2. Child Static Initializers execute -> "2. Child Static Block" |
+-------------------------------------------------------------------------------+
|
v
+-------------------------------------------------------------------------------+
| PHASE 2: ENTRY POINT EXECUTION |
| 3. main() starts -> prints "--- Main Starts ---" |
+-------------------------------------------------------------------------------+
|
v
+-------------------------------------------------------------------------------+
| PHASE 3: FIRST OBJECT INSTANTIATION: new Child() (<init>) |
| 4. Child.<init>() invokes super() -> Parent.<init>() |
| 5. Parent instance blocks inline -> "3. Parent Instance Block" |
| 6. Parent constructor body runs -> "4. Parent Constructor" |
| 7. Child instance blocks inline -> "5. Child Instance Block" |
| 8. Child constructor body runs -> "6. Child Constructor" |
+-------------------------------------------------------------------------------+
|
v
+-------------------------------------------------------------------------------+
| PHASE 4: SECOND OBJECT INSTANTIATION: new Child() |
| Static blocks ARE SKIPPED (Classes already loaded in JVM Metaspace). |
| Repeats lines 3 through 6 for the second heap object. |
+-------------------------------------------------------------------------------+
Detailed 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.
3. Bytecode Synthesis: <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
}
4. Crucial Edge Case: Exceptions in Static Initializers
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!
5. Runnable Verification Code
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();
}
}
6. Concise Staff-Level Interview Answer
"The execution order is governed by the two distinct JVM lifecycle phases: class loading (
<clinit>) and instance initialization (<init>).When
Child.main()is invoked, the JVM must first initializeChild.class. Because initialization requires superclass readiness,Parent's static blocks run first, followed byChild's static blocks. Only after class loading completes doesmain()execute its first line.When
new Child()is evaluated, control entersChild.<init>(), which immediately delegates tosuper()(Parent.<init>()). In bytecode, instance initializer blocks and field declarations are inlined directly into constructors right after thesuper()call. Thus,Parent Instance Blockruns, followed byParent Constructor. Control then returns toChild, executingChild Instance Blockand finallyChild Constructor.On the second instantiation, static blocks do not re-run because classes are already loaded and verified in Metaspace; only the instance blocks and constructors execute."