почему статический блок не может получить доступ к статической переменной, определенной после него

Я проверил прямые ссылки Во время инициализации поля и этого ответа от @assylias, но до сих пор я не получил ответа на почему.

Почему статический блок может присвоить статическую переменную, объявленную после него, но не может НЕ получить к ней доступ?

   class Parent {
        static {
            i = 2; // valid
            // can only assign new value to it instead of accessing it?
//            System.out.println(i); // invalid - compile-error
        }
        static int i = 0;
        static {
            i = 3; // valid
        }
    }

Это связано с тем, что значение еще не инициализировано, поэтому мы просто явно запрещаем вам его использовать? или есть что-то связанное с безопасностью, которого я не знаю?


обновлен

это не дубликат той проблемы, о которой идет речь

Почему этого не происходит при доступе с имя класса?

Этот вопрос о том, почему у нас есть этот дизайн? для чего?


person Hearen    schedule 19.03.2019    source источник
comment
будет compile error, как уже упоминалось @Lino, поэтому вы просто не можете получить байт-код   -  person Hearen    schedule 19.03.2019
comment
@Lino Этот код не компилируется. Нет байт-кода для проверки. Не размещайте здесь бессмысленные догадки.   -  person user207421    schedule 19.03.2019
comment
Да, потому что он еще не инициализирован, о чем говорит сообщение об ошибке, не так ли?   -  person user207421    schedule 19.03.2019
comment
@Lino, пожалуйста, проверьте обновление для duplication проблемы   -  person Hearen    schedule 19.03.2019
comment
@Hearen Задать вопрос, почему были приняты языковые решения, нелегко ответить на вопросы. Это требует довольно глубоких знаний от одного из разработчиков Java, и если Брайан Гетц не появится, я не уверен, что кто-нибудь здесь может ответить на этот вопрос.   -  person Lino    schedule 19.03.2019
comment
Спасибо за помощь. Извините, что публикую такой вопрос. Но во-первых, это не дубликат; во-вторых, я знаю, что этот вопрос не простой (даже как вы сказали, невозможный вопрос), и поэтому добавляю это сообщение в вопрос, когда я его начинаю: будут отрицательные голоса, но я просто потерял подсказку в лесу. который удаляется лук2302.   -  person Hearen    schedule 19.03.2019
comment
@Lino Спасибо за помощь на этом пути. Моя идея проста: я хочу изучить это и понять это. Извините за беспокойство :) хорошего дня ;)   -  person Hearen    schedule 19.03.2019


Ответы (3)


Статические поля инициализируются на основе порядка их появления в коде.

Итак, когда вы присваиваете значение переменной i, вы просто говорите компилятору: «Эй, парень, когда ты будешь инициализировать эту переменную, установи ее значение в…». Но вы не можете использовать его, пока он не будет инициализирован, потому что его просто еще не существует.

ОБНОВЛЕНИЕ:

Как сказано в книге «Спецификация языка Java» Джеймса Гослинга, Билла Джоя, Гая Стила и Гилада Брачи:

Эти ограничения предназначены для обнаружения во время компиляции циклических или иным образом искаженных инициализаций.

Учти это:

static {
            i = 2;
            j = i + 5; //should it be 7 or 15?
}
static int i = 10;
static int j;

Должна ли переменная j быть 7 или 15? Если 7, то мы дважды инициализировали переменную i, что невозможно, так как поле статичное. Если 15, то что означает i = 2;?

Этот код неоднозначен, поэтому Спецификация Java не позволяет этого сделать.

person Pavel Smirnov    schedule 19.03.2019
comment
Дал некоторые пояснения. - person Pavel Smirnov; 19.03.2019
comment
Вау, спасибо большое. Я просто заблудился и понятия не имею, почему это запрещено. Этот пример дает мне подсказку. Большое спасибо! Павел - person Hearen; 19.03.2019
comment
«тогда мы дважды инициализировали переменную i, что невозможно, так как поле статическое», кхм, а почему вы думаете, что это невозможно? Вы можете назначать переменную так часто, как хотите, если только это не final. Вы можете легко написать static int i = 2, j = i++ + 5; static { i += 3; } Или изменить j = i + 5; в своем собственном примере на j = ClassName.i + 5;, и это сработает, поскольку переменная действительно уже существует. - person Holger; 20.03.2019
comment
Так сказано в спецификации Java: во время выполнения инициализатор оценивается, и присваивание выполняется ровно один раз, когда инициализируется класс. Здесь docs.oracle.com /javase/specs/jls/se8/html/jls-8.html#jls-8.3.3 и здесь docs.oracle.com/javase/specs/jls/se8/html/ О ссылке на переменную по ее полному имени . В этом случае компилятор точно знает, что класс уже инициализирован. Этот вопрос обсуждался здесь: stackoverflow.com/questions/29153703/ - person Pavel Smirnov; 20.03.2019

После некоторого дальнейшего чтения я думаю, что Павел не совсем точен в этом вопросе, поскольку @Holger указал в комментарии.

мы дважды инициализировали переменную i, что невозможно, так как поле статическое.

Согласно 12.4.2. Подробная процедура инициализации указывает

Для каждого класса или интерфейса C существует уникальная блокировка инициализации LC. Отображение из C в LC оставлено на усмотрение реализации виртуальной машины Java.

Я полагаю, что инициализация дважды подходит для самого инициализатора класса, если только один раз для вызывающих клиентов.

Но демонстрация, предоставленная Павлом, по-прежнему сохраняет свою позицию, поэтому я в основном просто использую ее здесь, но с другим объяснением.

static {
       i = 2;
       j = i + 5; 
       // no one knows whether "i" here initialized properly here
}
static int i = 10;
static int j;

Но когда вы используете MyClass.i непосредственно в j = MyClass.i + 5, компилятор будет знать, что все будет в порядке, как 8.3.3. Пересылка ссылок во время инициализации поля с четырьмя условиями.

В частности, это ошибка времени компиляции, если выполняются все следующие утверждения:

  1. Объявление переменной класса в классе или интерфейсе C появляется текстуально после использования переменной класса;

  2. Использование — простое имя либо в инициализаторе переменной класса C, либо в статическом инициализаторе C;

  3. Использование не находится в левой части присваивания;

  4. C является самым внутренним классом или интерфейсом, включающим использование.

И в этом ответе уже есть подробное обсуждение.

В заключение я думаю, что для предсказуемого поведения нужно добавить эти ограничения. Еще раз, другая официальная цель указана в 8.3.3. Пересылка ссылок во время инициализации поля.

Эти ограничения предназначены для обнаружения во время компиляции циклических или иным образом искаженных инициализаций.

person Hearen    schedule 27.03.2019

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

class Parent {
    {
        x = 3; // works fine
        // System.out.println(x); // gives compilation error.

    }
    int x = 0;

    public static void main(String[] args) {

    }
}
person Sumit    schedule 20.06.2020