Does calling a static method on a class in Java trigger the static initalization blocks to get executed?
Empirically, I'd say no. I have something like this:
public class Country {
static {
init();
List<Country> countries = DataSource.read(...); // get from a DAO
addCountries(countries);
}
private static Map<String, Country> allCountries = null;
private static void init() {
allCountries = new HashMap<String, Country>();
}
private static void addCountries(List<Country> countries) {
for (Country country : countries) {
if ((country.getISO() != null) && (country.getISO().length() > 0)) {
allCountries.put(country.getISO(), country);
}
}
}
public static Country findByISO(String cc) {
return allCountries.get(cc);
}
}
In the code using the class, I do something like:
Country country = Country.findByISO("RO");
The problem is that I get a NullPointerException
because the map (allCountries
) is not initialized. If I set up breakpoints in the static
block I can see the map getting populated correctly, but it's as if the static method has no knowledge of the initializer being executed.
Can anyone explain this behavior?
Update: I've added more detail to the code. It's still not 1:1 (there are several maps in there and more logic), but I've explicitly looked at the declarations/references of allCountries
and they are as listed above.
You can see the full initialization code here.
Update #2: I tried to simplify the code as much as possible and wrote it down on the fly. The actual code had the static variable declaration after the initializer. That caused it to reset the reference, as Jon pointed out in the answer below.
I modified the code in my post to reflect this, so it's clearer for people who find the question. Sorry about the confusion everyone. I was just trying to make everyone's life easier :).
Thanks for your answers!
Does calling a static method on a class in Java trigger the static initalization blocks to get executed?
Empirically, I'd say no.
You're wrong.
From the JLS section 8.7:
A static initializer declared in a class is executed when the class is initialized (§12.4.2). Together with any field initializers for class variables (§8.3.2), static initializers may be used to initialize the class variables of the class.
Section 12.4.1 of the JLS states:
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.
This is easily shown:
class Foo {
static int x = 0;
static {
x = 10;
}
static int getX() {
return x;
}
}
public class Test {
public static void main(String[] args) throws Exception {
System.out.println(Foo.getX()); // Prints 10
}
}
Your problem is in some part of the code that you didn't show us. My guess is that you're actually declaring a local variable, like this:
static {
Map<String, Country> allCountries = new HashMap<String, Country>();
// Add entries to the map
}
That hides the static variable, leaving the static variable null. If this is the case, just change it to an assignment instead of a declaration:
static {
allCountries = new HashMap<String, Country>();
// Add entries to the map
}
EDIT: One point worth noting - although you've got init()
as the very first line of your static initializer, if you're actually doing anything else before then (possibly in other variable initializers) which calls out to another class, and that class calls back into your Country
class, then that code will be executed while allCountries
is still null.
EDIT: Okay, now we can see your real code, I've found the problem. Your post code has this:
private static Map<String, Country> allCountries;
static {
...
}
But your real code has this:
static {
...
}
private static Collection<Country> allCountries = null;
There are two important differences here:
The combination of those is messing you up: the variable initializers aren't all run before the static initializer - initialization occurs in textual order.
So you're populating the collection... and then setting the reference to null.
Section 12.4.2 of the JLS guarantees it in step 9 of the initialization:
Next, execute either the class variable initializers and static initializers of the class, or the field initializers of the interface, in textual order, as though they were a single block.
Demonstration code:
class Foo {
private static String before = "before";
static {
before = "in init";
after = "in init";
leftDefault = "in init";
}
private static String after = "after";
private static String leftDefault;
static void dump() {
System.out.println("before = " + before);
System.out.println("after = " + after);
System.out.println("leftDefault = " + leftDefault);
}
}
public class Test {
public static void main(String[] args) throws Exception {
Foo.dump();
}
}
Output:
before = in init
after = after
leftDefault = in init
So the solution is either to get rid of the explicit assignment to null, or to move the declarations (and therefore initializers) to before the static initializer, or (my preference) both.
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