Статические блоки инициализации

Рейтинг: 5Ответов: 1Опубликовано: 07.08.2023

В нижеприведенном коде есть 2 статических блока, почему компилятор не считает за ошибку то, что в первом блоке не указан тип данных у i? При этом, если при инициализации i = 0 убрать тип данных будет ошибка, и если указать тип данных только во втором блоке тоже будет ошибка.

public class Test {
    
    static {
        i = 10;
    }
    
    static int i = 0;

    static {
        ++i;
    }
    
    public static void main(String[] args) {
        new Test();
        System.out.println(i);
    }
}

Ответы

▲ 6Принят

Компилятор сначала обработает объявление статической переменной, а уже потом будут выполнены блоки статической инициализации и/или присваивания статической переменной в порядке их определения в коде. В частности, это приведёт к тому, что присваивание 10 в первом блоке будет утеряно.

Порядок таков:

  1. Объявляется статическая переменная i.
  2. Выполняется первый блок статической инициализации static {i = 10;}
  3. Выполняется присваивание при объявлении переменной static int i = 0
  4. Выполняется второй блок статической инициализации static {++i; // 1}

Но если бы вы попытались обратиться в первом блоке статической инициализации к переменной (например, System.out.println(i);), тогда возникла бы ошибка компиляции, связанная с обращением к неопределённой переменной.

static {
    System.out.println("1st block in: i=" + i);  // error: illegal forward reference
    i = 10;
    System.out.println("1st block out: i=" + i); // error: illegal forward reference
}

static int i = 0;

См. Which cases allow forward referencing in static initializer blocks?


При этом, если при инициализации i = 0 убрать тип данных будет ошибка,

Разумеется, тогда у вас получается неправильно объявленная статическая переменная (не указан тип).

и если указать тип данных только во втором блоке тоже будет ошибка.

В таком случае вы объявляете локальную переменную внутри блока статической инициализации, изменения в которой не повлияют на значение переменной класса.

Модифицированный пример:

static {
    int i = 10;
    System.out.println("1st block out: i=" + i); // локальная переменная блока 1
}

static int i = 5;

static {
    System.out.println("2nd block in: i=" + i);  // переменная класса
    int i = 20;
    System.out.println("2nd block out: i=" + i); // локальная переменная блока 2
}

public static void main(String[] args) {
    System.out.println(i);
}

Результат:

1st block out: i=10
2nd block in: i=5
2nd block out: i=20
5