Why Accessing a Superclass Static Field Skips Subclass Initialization – JVM Class Loading Explained
This article examines common Java interview questions about static field access, demonstrates how the JVM loads and initializes classes, explains why a subclass may not be initialized when referencing a superclass static variable, and details each phase of the JVM class‑loading lifecycle.
The article starts with two typical Java interview questions that test understanding of the JVM class‑loading mechanism, especially the behavior of static fields.
Interview Question 1 – Accessing a Non‑final Static Field
public class SuperClass {
static {
System.out.println("SuperClass static init");
}
public static String ABC = "abc";
}
public class SubClass extends SuperClass {
static {
System.out.println("SubClass static init");
}
}
public class Main {
public static void main(String[] args) {
System.out.println(SubClass.ABC);
}
}Running the program prints:
SuperClass static init
abcThe output shows that only SuperClass is initialized. The JVM initializes the class that actually defines the referenced static field; accessing SubClass.ABC triggers initialization of SuperClass but not SubClass because the field belongs to the superclass.
Interview Question 2 – Adding final to the Static Field
public class SuperClass {
static {
System.out.println("SuperClass static init");
}
public static final String ABC = "abc";
}
public class SubClass extends SuperClass {
static {
System.out.println("SubClass static init");
}
}
public class Main {
public static void main(String[] args) {
System.out.println(SubClass.ABC);
}
}The program now prints only:
abcBecause the field is final, the compiler treats it as a constant and inlines its value into the calling class. The reference no longer requires loading SuperClass, so the static initializer is never executed.
JVM Class‑Loading Process
The JVM loads class data from a .class file (or other sources), verifies it, prepares it, resolves symbolic references, initializes it, uses it, and eventually unloads it. Loading, verification, preparation, and initialization occur before the class can be used; resolution may be deferred until after initialization to support dynamic binding.
Loading Phase
Obtain the binary byte stream of the class using its fully‑qualified name (from a .class file, JAR, network, etc.).
Convert the byte stream into the runtime data structures stored in the method area.
Create a java.lang.Class object that serves as the entry point for accessing the class’s metadata.
Verification Phase
The JVM checks that the class file conforms to the specification and does not threaten VM security. Verification includes:
File‑format verification (magic number, version, constant‑pool validity).
Metadata verification (semantic checks such as correct inheritance, method signatures, and access flags).
Bytecode verification (data‑flow and control‑flow analysis to ensure type safety and proper instruction usage).
Symbolic reference verification (ensuring that constant‑pool symbolic references can be resolved).
Preparation Phase
During preparation the JVM allocates memory for static variables in the method area and assigns them default values. For example:
public static String ABC = "abc";If the variable is declared final, the constant value is stored directly during this phase.
Resolution Phase
Resolution replaces symbolic references in the constant pool with direct references. When the JVM resolves a field reference such as SubClass.ABC, it first looks for the field in SubClass; if not found, it recursively searches superclasses and interfaces. Failure results in java.lang.NoSuchFieldError or java.lang.IllegalAccessError.
Initialization Phase
Initialization executes the class’s <clinit>() method, which consists of all static variable initializations and static blocks, ordered as they appear in the source file. The parent class’s <clinit>() runs before the child’s. The JVM guarantees that only one thread executes <clinit>() for a given class, blocking other threads until completion.
VM Specification – When Initialization Occurs (JDK 1.7)
Execution of new, getstatic, putstatic, or invokestatic on a class that has not yet been initialized.
Reflective operations via java.lang.reflect that access a class.
When initializing a class, if its superclass has not been initialized, the superclass is initialized first.
The main class specified for program start is initialized first.
Dynamic language support (method handles) that resolve to static fields or methods of an uninitialized class.
Conclusion
Understanding these steps clarifies why static field access can trigger or bypass class initialization and prepares readers for deeper topics such as class loaders and the parent‑delegation model.
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
Senior Brother's Insights
A public account focused on workplace, career growth, team management, and self-improvement. The author is the writer of books including 'SpringBoot Technology Insider' and 'Drools 8 Rule Engine: Core Technology and Practice'.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
