I think that only Base class is initialized when Derived.f()
is called. Exactly like it happens when we have a (non-compile-time-constant) static field in Base, not static method. My doubt is only that JLS is not very clear on that.
class Base {
static void f() {}
}
class Derived extends Base {
}
Derived.f(); // called legally from some other class
According to JLS (see below), it seems that only Base class is initialized. But do I read and understand JLS correctly?
I also know that in the case with (non-compile-time-constant) static fields only class where static field is defined is initialized (static initializers called):
class Base {
static int x = 1;
}
class Derived extends Base {
}
//somewhere in some other class
int y = Derived.x; // triggers only Base to be initialized
12.4.1. When Initialization Occurs (JLS)
A class or interface type T will be initialized immediately before the first occurrence of any one of the following:
T is a class and an instance of T is created.
T is a class and a static method declared by T is invoked.
A static field declared by T is assigned.
A static field declared by T is used and the field is not a constant variable (§4.12.4).
T is a top level class (§7.6), and an assert statement (§14.10) lexically nested within T (§8.1.3) is executed.
A reference to a static field (§8.3.1.1) causes initialization of only the class or interface that actually declares it, even though it might be referred to through the name of a subclass, a subinterface, or a class that implements an interface. BUT JLS DOES NOT STATE HERE THE SAME ABOUT STATIC METHODS THOUGH !
Besides, 12.4. Initialization of Classes and Interfaces says clearly:
Before a class is initialized, its direct superclass must be initialized
And an exclusion from this rule is given by JLS only for static fields, not static methods!
Initializing both Base and Derived seemingly would make sense - if f() uses any static fields inside its body, then Derived.f() will be using static fields of Derived (if any), and may use static fields inherited from Base (if Derived has none) - which makes sense to initialize both classes.
After all, simple test shows that only Base class is initialized:
class Base {
static { System.out.println("Base init"); }
static void f() {}
}
class Derived extends Base {
static { System.out.println("Derived init"); }
}
public class Driver {
public static void main(String[] args) {
Derived.f(); // Only "Base init" printed...
}
}