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!
The static initializer will get called when the class is loaded, which is normally when it is first 'mentioned'. So calling a static method would indeed trigger the initializer if this is the first time that the class gets referenced.
Are you sure the null pointer exception is from the
allcountries.get()
, and not from a nullCountry
returned byget()
? In other words, are you certain which object is null?Theoretically, static block should get executed by the time classloader loads the class.
In your code, it is initialized the first time you mention the class Country (probably the line above).
I ran this:
with this:
and everything worked correctly. Can you post the stack trace of the error?
I would say that the problem lies in the fact that the static block is declared before the actual field.
Do you have
allCountries = new HashMap();
in your static initializer block? The static initializer block is actually called upon class initialization.You're wrong.
From the JLS section 8.7:
Section 12.4.1 of the JLS states:
This is easily shown:
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:
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:
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 yourCountry
class, then that code will be executed whileallCountries
is still null.EDIT: Okay, now we can see your real code, I've found the problem. Your post code has this:
But your real code has this:
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:
Demonstration code:
Output:
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.