В каком порядке выполняются блокировка инициализатора и определения переменных и т.д.? (в java)

У меня проблема с пониманием порядка, в котором происходит инициализация. это тот порядок, который я предполагал:

*Once per 
    1. Static variable declaration
    2. Static block
*Once per object
    3. variable declaration
    4. initialization block
    5. constructor

но в соответствии с этим кодом я, очевидно, ошибаюсь:

    class SomethingWrongWithMe
    {
        {
            b=0;         //no. no error here.
            int a = b;   //Error: Cannot reference a field before it is defined.
        }
        int b = 0;
    }

И ошибка исчезнет, ​​если я это сделаю:

    class SomethingWrongWithMe
    {
        int b = 0;
        {
            b=0;
            int a = b;   //The error is gone.
        }
    }

Я не могу понять, почему на

    b=0;

Ответ 1

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

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

Статики запускаются только один раз при первом использовании класса.

Ошибка компиляции представляется правилом Java, а не тем, что обязательно имеет смысл в каждом случае.

Ответ 2

Переменные определения не выполняются "до" блоков. Они оба выполняются одновременно, в том порядке, в котором они определены.

class SomethingWrongWithMe {
    {
        b = debug("block 1");
    }
    int b = debug("define");
    {
        b = debug("block 2");
    }
    private int debug(String str) {
        System.out.println(str);
        return 0;
    }
}

Выход

block 1
define
block 2

Ответ 3

Прежде всего, ваши предположения более или менее правильны, за исключением того факта, что объявления (с инициализацией, такие как int b = 0) и блоки инициализатора экземпляра выполняются в том порядке, в котором они записаны.

int b = 0;   // executed first

{
    b = 1;   // executed second
}

int a = b;   // executed third

Также обратите внимание, что объявление, т.е. int b, не выполняется. Объявление просто объявляет о существовании переменной.

Что касается полученной вами ошибки (или, скорее, ошибки, которую вы не получили), я согласен, что это выглядит странно. Я предполагаю, что компилятор имеет дело с ссылкой на переменную в выражении и присвоением ей значения по-разному. При записи в переменную в инициализаторе экземпляра он просто проверяет, есть ли переменная, а при чтении из нее требуется, чтобы она была объявлена ​​над блоком инициализатора экземпляра. Я посмотрю, могу ли я найти ссылку для этого в JLS.