A class or interface type T will be initialized at its first active use, which occurs if:
final
and static
, and that is initialized with the value of a compile-time constant expression (§15.27). Java specifies that a reference to a constant field must be resolved at compile time to a copy of the compile-time constant value, so uses of such a field are never active uses. See §13.4.8 for a further discussion.
All other uses of a type are passive uses.
The intent here is that a class or interface type has a set of initializers that put it in a consistent state, and that this state is the first state that is observed by other classes. The static initializers and class variable initializers are executed in textual order, and may not refer to class variables declared in the class whose declarations appear textually after the use, even though these class variables are in scope (§8.5). This restriction is designed to detect, at compile time, most circular or otherwise malformed initializations.
As shown in an example in §8.5, the fact that initialization code is unrestricted allows examples to be constructed where the value of a class variable can be observed when it still has its initial default value, before its initializing expression is evaluated, but such examples are rare in practice. (Such examples can be also constructed for instance variable initialization; see the example at the end of §12.5). Java provides the full power of the language in these initializers; programmers must exercise some care. This power places an extra burden on code generators, but this burden would arise in any case because Java is concurrent (§12.4.3).
Before a class is initialized, its superclasses are initialized, if they have not previously been initialized.
class Super { static { System.out.print("Super "); } } class One { static { System.out.print("One "); } } class Two extends Super { static { System.out.print("Two "); } } class Test { public static void main(String[] args) { One o = null; Two t = new Two(); System.out.println((Object)o == (Object)t); } }
Super Two false
The class One
is never initialized, because it not used actively and therefore is
never linked to. The class Two
is initialized only after its superclass Super
has
been initialized.
A reference to a field is an active use 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. The test program:
class Super { static int taxi = 1729; } class Sub extends Super { static { System.out.print("Sub "); } } class Test { public static void main(String[] args) { System.out.println(Sub.taxi); } }
1729
because the class Sub
is never initialized; the reference to Sub.taxi
is a reference
to a field actually declared in class Super
and is not an active use of the class Sub
.
Initialization of an interface does not, of itself, require initialization of any of its superinterfaces. Thus, the test program:
interface I { int i = 1, ii = Test.out("ii", 2); } interface J extends I { int j = Test.out("j", 3), jj = Test.out("jj", 4); } interface K extends J { int k = Test.out("k", 5); } class Test { public static void main(String[] args) { System.out.println(J.i); System.out.println(K.j); } static int out(String s, int i) { System.out.println(s + "=" + i); return i; } }
1 j=3 jj=4 3
The reference to J.i
is to a field that is a compile-time constant; therefore, it does
not cause I
to be initialized. The reference to K.j
is a reference to a field actually
declared in interface J
that is not a compile-time constant; this causes initialization of the fields of interface J
, but not those of its superinterface I
, nor those of
interface K
. Despite the fact that the name K
is used to refer to field j
of interface
J
, interface K
is not actively used.