Приводит ли вызов статического метода к классу в Java запуск статических блоков инициализации для выполнения?
Эмпирически, я бы сказал, нет.
Вы 'повторно неправильно.
Из раздела JLS 8.7 :
Статический инициализатор, объявленный в классе, выполняется при инициализации класса (§12.4.2),Вместе с любыми инициализаторами полей для переменных класса (§8.3.2) статические инициализаторы могут использоваться для инициализации переменных класса класса.
Раздел 12.4.1 изСостояния JLS:
Класс T или интерфейсный тип T будут инициализированы непосредственно перед первым появлением любого из следующего:
T является классом исоздается экземпляр T.
T является классом, и вызывается статический метод, объявленный T.
Статическое поле, объявленноеПрисвоено T.
Используется статическое поле, объявленное T, и это поле не является постоянной переменной (§4.12.4).
T является классом верхнего уровня (§7.6), и выполняется оператор assert (§14.10), лексически вложенный в T (§8.1.3).
Этолегко показать:
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
}
}
Ваша проблема в какой-то части кода, которую вы нам не показывали.Моя догадка заключается в том, что вы фактически объявляете локальную переменную, например:
static {
Map<String, Country> allCountries = new HashMap<String, Country>();
// Add entries to the map
}
То, что скрывает статическую переменную, оставляя статическую переменную нулевой.Если дело обстоит именно так, просто измените его на присваивание вместо объявления:
static {
allCountries = new HashMap<String, Country>();
// Add entries to the map
}
РЕДАКТИРОВАТЬ: Один пункт стоит отметить - хотя вы получили init()
в качестве самогопервой строке вашего статического инициализатора, если вы на самом деле делаете что-то еще до этого (возможно, в других инициализаторах переменных), которое вызывает другой класс, и этот класс вызывает back в вашCountry
class, тогда этот код будет выполняться, пока allCountries
по-прежнему равен нулю.
EDIT: Хорошо, теперь мы можем увидеть ваш реальный код, я обнаружил проблему.Ваш post код имеет это:
private static Map<String, Country> allCountries;
static {
...
}
Но ваш real код имеет это:
static {
...
}
private static Collection<Country> allCountries = null;
Есть two важные отличия здесь:
- Объявление переменной происходит после статического блока инициализации
- Объявление переменной включает явное присвоение нулю
Комбинация из них запутывает вас: инициализаторы переменных не запускаются до статического инициализатора - инициализация происходит в текстовом порядке .
Итак, вы заполняетеcollection ... и затем установка ссылки на ноль.
Раздел 12.4.2 JLS гарантирует это на шаге 9 инициализации:
Далеевыполните инициализаторы переменных класса и статические инициализаторы класса или инициализаторы полей интерфейса в текстовом порядке, как если бы они были единым блоком.
Код демонстрации:
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();
}
}
Выход:
before = in init
after = after
leftDefault = in init
Таким образом, решение - или , чтобы избавиться от явного присвоения нулю, или для перемещения объявлений (и, следовательно, инициализаторов) до статического инициализатора, или(мои предпочтения) оба.