Can I call a static method from a static initializer in Java? Is the following valid and guaranteed to work as per the Java specification?
public class Foo {
private final static int bar;
private static int generateValue() {
return 123;
}
static {
bar = generateValue();
}
}
What makes me wonder is that I might expect bar
to be available inside generateValue()
. I know the order of static initializer blocks is important, but I hadn't heard of the order of static method declarations being significant. But are static methods made available before static initializer blocks are executed?
As @Mureinik said, "In a word -yes. This code is perfectly legal." I want to provide a more thorough answer because you can easily develop problems when using static initializers in conjunction with class methods--the order of appearance can affect the class state, and it's a nasty bug to track down.
A static initializer declared in a class is executed when the class is initialized. Together with any field initializers for class variables... static initializers may be used to initialize the class variables of the class -- Java Language Specification (JLS) §8.7
Initialization generally takes place in the order of appearance (called textual ordering). For example, consider the following code:
class Bar {
static int i = 1;
static {i += 1;}
static int j = i;
}
class Foo {
static int i = 1;
static int j = i;
static {i += 1;}
public static void main(String[] args) {
System.out.println("Foo.j = " + Foo.j);
System.out.println("Bar.j = " + Bar.j);
}
}
The value for Foo.j
and Bar.j
are different because of the differences in the textual ordering of the code:
Foo.j = 1
Bar.j = 2
The OP's example executes in textual order. But what if the code were rearranged, say, in reverse order:
class Foo {
static { bar = generateValue(); } //originally 3rd
private static int generateValue() { return 123; } //originally 2nd
private final static int bar; //originally 1st
public static void main(String[] args) {
System.out.println("Foo.bar = " + Foo.bar);
}
}
It turns out, that this compiles without error. Furthermore, the output is: Foo.bar = 123
. So, bar
does in fact contain 123
at runtime. However, the following code (from JLS §8.3.1.1) produces a compile time error because it tries to access j
before j
is declared:
//Don't do this!
class Z {
static { i = j + 2; } //Produces a compilation error
static int i, j;
static { j = 4; }
}
Interestingly, accesses by methods are not checked in this way, so:
class Foo {
static int peek() { return j; }
static int i = peek();
static int j = 1;
public static void main(String[] args) {
System.out.println("Foo.i = " + Foo.i);
}
}
produces the following output:
Foo.i = 0
this happens because
the variable initializer for
i
uses the class methodpeek
to access the value of the variablej
beforej
has been initialized by its variable initializer, at which point it still has its default value -- JLS §8.3.2.3
If instead, i
is initialized after j
, then the output is
Foo.i = 1
And the situation gets worse when using objects instead of primitive types, as in:
class Foo { //Don't do this
static int peek() { return j.hashCode(); } // NullPointerException here
static int i = peek();
static Object j = new Object();
public static void main(String[] args) {
System.out.println("Foo.i = " + Foo.i);
}
}
This peek
throws a NullPointerException
while initializing i
:
Exception in thread "main" java.lang.ExceptionInInitializerError
Caused by: java.lang.NullPointerException
at TestGame.Foo.peek(Foo.java:4)
at TestGame.Foo.<clinit>(Foo.java:5)
Eclipse pops-up this window when the above code is run:
Tying this back to the OP, if instead of returning 123
, the generateValue()
method returned the value of some other static field (or method), then the value of bar
depends on the textual ordering of the code.
So, when is textual ordering important?
Textual ordering is not always used. Sometimes the JVM looks-ahead to perform initialization. It's important to know when look-ahead is possible, and when initialization occurs in textual order. The JLS describes the Restrictions on the use of Fields during Initialization in §8.3.2.3 (emphasis my own):
The declaration of a member needs to appear textually before it is used only if the member is [a] ...static field of a class or interface C and all of the following conditions hold:
- The usage occurs in [a]... static variable initializer of C or in [a] ...static initializer of C.
- The usage is not on the left hand side of an assignment.
- The usage is via a simple name.
- C is the innermost class or interface enclosing the usage.
One last note: constants are initialized first (per JLS §8.3.2.1):
At run time, static fields that are final and that are initialized with constant expressions (§15.28) are initialized first (§12.4.2).
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With