Как осуществляется порядок выполнения между статическими переменными и блоками?

Рассмотрим следующий сценарий:
Код: 1

public class StaticDemo {
    static{
        b=5;
        System.out.println("Static B:"+b);/*Compilation error:"Cannot reference a field before it is defined"*/
    }
    static int b;
    static{
        System.out.println("B:"+b);
    }
    public static void main(String[] args) {    
    }
}

Комментируя код, как показано ниже, ошибок нет, и отображается следующий вывод.
Код: 2

public class StaticDemo {
    static{
        b=5;
        //System.out.println("Static B:"+b);
    }
    static int b;
    static{
        System.out.println("B:"+b);
    }
    public static void main(String[] args) {    
    }
}

Вывод-

B:5

Если выполнение основано на порядке записи статических переменных или блоков.

  1. почему ошибка компиляции не возникает при инициализации (b=5), как показано в коде: 2.

  2. А также, пожалуйста, объясните, почему выдается ошибка для кода: 1, если код: 2 верен?


person Vasanth    schedule 25.03.2014    source источник
comment
На этот вопрос дан ответ stackoverflow.com /вопросы/16635200/   -  person Oliver    schedule 25.03.2014


Ответы (3)


Из Спецификации языка Java, §8.3. .2.3

Объявление члена должно отображаться в текстовом виде перед его использованием только в том случае, если член является экземпляром (соответственно статическим) полем класса или интерфейса C и выполняются все следующие условия:

  • Использование происходит в экземпляре (соответственно статическом) инициализаторе переменной C или в экземпляре (соответственно статическом) инициализаторе C.
  • Использование не находится в левой части присваивания.
  • Использование через простое имя.
  • C — это самый внутренний класс или интерфейс, охватывающий использование.

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

[...]

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

Другими словами, можно записывать в поле, объявленное позже в классе, но не читать из него.

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

person Ian Roberts    schedule 25.03.2014
comment
+1: Побей меня. Только что потратил десять минут на поиски ссылки на спецификацию ›.‹ - person Rudi Kershaw; 25.03.2014
comment
@ian-roberts Большое спасибо. - person Vasanth; 25.03.2014
comment
@Vasanth - Если вы найдете ответ полезным, не забудьте также проголосовать за него. :) - person Rudi Kershaw; 25.03.2014
comment
@Vasanth - Нет проблем. - person Rudi Kershaw; 25.03.2014

В первом примере переменная b инициализируется первым статическим блоком, который выполняется первым. Компилятор не помнит первую строку, поэтому во второй строке статического блока он ссылается на b, поскольку 'b' может быть еще не инициализирован.

Когда компилятор передал определение b, он уверен, что b инициализирован. Следовательно, вашему коду разрешено ссылаться на него/доступ к нему.

Инициализировать статическую переменную до того, как она появится в исходном коде, можно, но доступ к ней разрешен только после того, как она произошла.

Если вы обратитесь к спецификации Java в нем указано следующее:

Initialization of a class consists of executing its static initializers and the initializers for static fields (class variables) declared in the class. 

A class or interface type T will be initialized immediately before the first occurrence of any one of the following:
    1) A static field declared by T is assigned.
    2) A static field declared by T is used and the field is not a constant variable (§4.12.4).

1) Говорит, что статический инициализатор выполняется перед непосредственным назначением статических полей. 2) Говорит, что прежде чем вы сможете использовать поле, должны быть выполнены инициализаторы (блоки и инициализатор поля).

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

person Harmlezz    schedule 25.03.2014
comment
Инициализировать статическую переменную до того, как она появится в исходном коде, можно, но доступ к ней разрешен только после ее появления. Я могу понять, что вы пытаетесь сказать. Но не могли бы вы предоставить какие-либо ссылки или источники, объясняющие этот сценарий. Это было бы очень полезно. - person Vasanth; 25.03.2014

Порядок выполнения сверху вниз. Вы не можете использовать статическое поле до его объявления.

почему выдается ошибка для кода: 1, если код: 2 верен

Вы попытались использовать переменную до того, как она была объявлена. Кстати, во время выполнения не возникла ошибка. Это ошибка компилятора.

person Peter Lawrey    schedule 25.03.2014
comment
Если код снизу, как можно присвоить значение переменной, которая еще не объявлена? - person praveen_mohan; 25.03.2014
comment
В яблочко. Должен признаться, я не знал, что это возможно. Код 2 действительно компилируется, а Код 1 — нет. - person Erwin Bolwidt; 25.03.2014
comment
Он был объявлен, переменные класса распознаются до запуска статических блоков. - person Rudi Kershaw; 25.03.2014
comment
@RudiKershaw Пожалуйста, будьте более точны в терминах, которые вы используете. «распознанный» ничего не объясняет - в коде 1 b распознается в b = 5, но не в операторе печати. - person Erwin Bolwidt; 25.03.2014
comment
@ErwinBolwidt - я просто имею в виду, что b уже объявлено, потому что статические блоки запускаются после объявления. Ошибка компиляции возникает из-за недопустимого прямого исключения (использование статической не конечной переменной во время инициализации). - person Rudi Kershaw; 25.03.2014