Recently I wrote a Java class that maps ISO 3166-1-alpha-2 country code to country name and encountered a static constructor initialization problem. My code is as following:
import java.util.Collection; import java.util.HashMap; public class Country { private final static HashMap<String,String> countryMap; public final static Country XX = new Country("Unknown"); public final static Country MY = new Country("MY", "Malaysia"); public final static Country SG = new Country("SG", "Singapore"); static { countryMap = new HashMap<String,String>(); } public static Collection values() { return countryMap.values(); } public static Country find(String code) { if (countryMap.containsKey(code)) return countryMap.get(code); else return XX; } private String code; private String name; public Country(String name) { this.code = null; this.name = name; } public Country(String code, String name) { this.code = code; this.name = name; countryMap.put(code, this); } public String getCode() { return code; } public void setCode(String code) { this.code = code; } public String getName() { return name; } public void setName(String name) { this.name = name; } }
It seems everything is correct to me. During runtime, the class throw an NullPointerException at line countryMap.put(code, this);
. Looks like the static constructor is not called and countryMap is not initialized. What I expected during runtime is that the static constructor will always run first before the others.
When refered to Java documentation at http://java.sun.com/docs/books/tutorial/java/javaOO/initial.html, I still can’t get any hint. After read and re-read the documentation a few times, finally I found the keywords: …The runtime system guarantees that static initialization blocks are called in the order that they appear in the source code…. So for the above code the static variable XX
and MY
are actually initialize first. Therefore the order of calling the static constructor is important in this case.
I rewrote the code and it runs without any problem. The correct code is as following:
import java.util.Collection; import java.util.HashMap; public class Country { private final static HashMap<String,String> countryMap; static { countryMap = new HashMap<String,String>(); } public final static Country XX = new Country("Unknown"); public final static Country MY = new Country("MY", "Malaysia"); public final static Country SG = new Country("SG", "Singapore"); public static Collection values() { return countryMap.values(); } public static Country find(String code) { if (countryMap.containsKey(code)) return countryMap.get(code); else return XX; } private String code; private String name; public Country(String name) { this.code = null; this.name = name; } public Country(String code, String name) { this.code = code; this.name = name; countryMap.put(code, this); } public String getCode() { return code; } public void setCode(String code) { this.code = code; } public String getName() { return name; } public void setName(String name) { this.name = name; } }
Or simply
import java.util.Collection; import java.util.HashMap; public class Country { private final static HashMap<String,String> countryMap = new HashMap<String,String>(); public final static Country XX = new Country("Unknown"); public final static Country MY = new Country("MY", "Malaysia"); public final static Country SG = new Country("SG", "Singapore"); public static Collection values() { return countryMap.values(); } public static Country find(String code) { if (countryMap.containsKey(code)) return countryMap.get(code); else return XX; } private String code; private String name; public Country(String name) { this.code = null; this.name = name; } public Country(String code, String name) { this.code = code; this.name = name; countryMap.put(code, this); } public String getCode() { return code; } public void setCode(String code) { this.code = code; } public String getName() { return name; } public void setName(String name) { this.name = name; } }
[…] Java Static Constructor […]