Создание объекта с использованием статического ключевого слова в Java

class abc {
    int a = 0;
    static int b;
    static abc h = new abc(); //line 4

    public abc() {
        System.out.println("cons");
    }

    {
        System.out.println("ini");
    }

    static {
        System.out.println("stat");
    }
}

public class ques {
    public static void main(String[] args) {
        System.out.println(new abc().a);
    }
}

Когда я написал этот код, я получаю вывод в следующем порядке:

ini
cons
stat
ini
cons
0

Здесь, когда я создал новый объект в main(), class abc, был загружен, а переменные static и блоки выполняются в порядке их написания. Когда управление переходит к строке 4 static abc h = new abc(); Вызывается блок инициализации экземпляра. Зачем? почему статический блок не вызывается, когда новый объект создается в строке 4, и до этого времени статический блок также не вызывался даже один раз, поэтому в соответствии с соглашением должен был быть вызван статический блок. Почему этот неожиданный выход идет?

Ответы

Ответ 1

Инициализация статических полей и статические блоки выполняются в том порядке, в котором они объявлены. В вашем случае код эквивалентен этому после разделения объявления и инициализации:

class abc{
    int a;
    static int b;
    static abc h;//line 4

    static {
        h = new abc();//line 4 (split)
        System.out.println("stat");
    }

    public abc() {
        a = 0;
        System.out.println("ini");
        System.out.println("cons");
    }
}

public class ques{
    public static void main(String[] args) {
        System.out.println(new abc().a);
    }
}

Итак, когда вы достигаете строки 4 из вашего кода, статическая инициализация фактически выполняется и еще не закончена. Следовательно, ваш конструктор вызывается до того, как stat может быть напечатан.

Ответ 2

JLS говорит:

Выполняются статические инициализаторы и инициализаторы переменных класса в текстовом порядке и не может ссылаться на переменные класса, объявленные в класс, чьи объявления появляются после использования, хотя даже эти переменные класса входят в сферу действия (§8.3.2.3). Это ограничение предназначенный для обнаружения во время компиляции большинства круговых или иных неправильные инициализации.

Это именно ваш случай.

Вот ваш оригинальный пример: http://ideone.com/pIevbX - статический инициализатор abc идет после назначения статического экземпляра abc - так статический инициализатор не может быть выполнен - ​​он текстовый после инициализации статической переменной

Переместить строку 4 после статического блока инициализации - http://ideone.com/Em7nC1:

class abc{
int a = 0;
static int b;
public abc() {
    System.out.println("cons");
}
{
    System.out.println("ini");
}
static {
    System.out.println("stat");
}
static abc h = new abc();//former line 4

}

Теперь вы можете увидеть следующий вывод:

stat
ini
cons
ini
cons
0

Теперь порядок инициализации больше похож на ожидаемый - сначала называется статический инициализатор, а затем статический экземпляр abc инициализируется обычным образом.