Когда инициализируются статические переменные?

87

Мне интересно, когда статические переменные инициализируются значениями по умолчанию. Верно ли, что при загрузке класса создаются (выделяются) статические переменные, затем выполняются статические инициализаторы и инициализации в объявлениях? В какой момент приводятся значения по умолчанию? Это приводит к проблеме прямой ссылки.

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

Анкит
источник
2
Не могли бы вы отредактировать свой вопрос, включив в него цитату, о которой вы говорите.
Оливер Чарльзуорт
1
Вы читали спецификацию языка Java? Это намеренно хорошо читаемый документ. Если вы прочитали это, вы могли понять, что происходит. Если нет, вы можете задать как минимум более конкретный вопрос ...
Маартен Бодевес,
Я думаю, что эти вопросы и ответы - дубликат stackoverflow.com/questions/3499214 .
Stephen C

Ответы:

72

Из См. Методы статических переменных Java :

  • Это переменная, которая принадлежит классу, а не объекту (экземпляру)
  • Статические переменные инициализируются только один раз, в начале выполнения. Эти переменные будут инициализированы первыми, перед инициализацией любых переменных экземпляра.
  • Единая копия для всех экземпляров класса
  • Доступ к статической переменной можно получить напрямую по имени класса и не требует никаких объектов.

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

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

В случае внутренних классов у них не может быть статических полей

Внутренний класс является вложенным классом , который не явно или неявно объявляется static.

...

Внутренние классы не могут объявлять статические инициализаторы (§8.7) или интерфейсы-члены ...

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

См. JLS 8.1.3 Внутренние классы и закрывающие экземпляры

finalПоля в Java могут быть инициализированы отдельно от места их объявления, однако это не может быть применимо к static finalполям. См. Пример ниже.

final class Demo
{
    private final int x;
    private static final int z;  //must be initialized here.

    static 
    {
        z = 10;  //It can be initialized here.
    }

    public Demo(int x)
    {
        this.x=x;  //This is possible.
        //z=15; compiler-error - can not assign a value to a final variable z
    }
}

Это происходит потому , что есть только одна копия из staticпеременных , связанных с типом, а не один , связанным с каждым экземпляром типа , как с переменным экземпляром и если мы попытаемся инициализировать zтип static finalв конструкторе, он будет пытаться повторно инициализировать static finalполе типа zпотому что конструктор запускается при каждой реализации класса, что не должно происходить со статическими finalполями.

Лев
источник
5
In case of static inner classes, they can not have static fieldsпохоже на опечатку. Внутренние классы нестатичны.
Daniel Lubarov
Вы должны использовать, однако, вместо Хотя
Сурадж Джайн
Когда вы запускаете JVM и загружаете класс в первый раз (это выполняется загрузчиком классов, когда на класс впервые ссылаются каким-либо образом), любые статические блоки или поля «загружаются» в JVM и становятся доступными.
nhoxbypass
1
К сожалению, этот ответ содержит некоторые фактические неточности в отношении инициализации статики. См. Stackoverflow.com/a/3499322/139985 .
Stephen C
15

Увидеть:

Последний, в частности, предоставляет подробные шаги инициализации, которые объясняют, когда инициализируются статические переменные и в каком порядке (с оговоркой, что finalпеременные класса и поля интерфейса, которые являются константами времени компиляции, инициализируются первыми).

Я не уверен, какой у вас конкретный вопрос по пункту 3 (если вы имеете в виду вложенный?). В подробной последовательности указано, что это будет рекурсивный запрос инициализации, поэтому он продолжит инициализацию.

Дэйв Ньютон
источник
11

Статические поля инициализируются, когда класс загружается загрузчиком классов. В это время присваиваются значения по умолчанию. Это делается в том порядке, в котором они указаны в исходном коде.

Дэйв
источник
10

Порядок инициализации:

  1. Блоки статической инициализации
  2. Блоки инициализации экземпляра
  3. Конструкторы

Детали процесса объяснены в документе спецификации JVM .

Оскар Лопес
источник
6

статическая переменная

  • Это переменная, которая принадлежит классу, а не объекту (экземпляру)
  • Статические переменные инициализируются только один раз, в начале выполнения (когда загрузчик классов загружает класс в первый раз).
  • Эти переменные будут инициализированы первыми, перед инициализацией любых переменных экземпляра.
  • Единая копия для всех экземпляров класса
  • Доступ к статической переменной можно получить напрямую по имени класса, и ей не нужен какой-либо объект.
алерут
источник
4

Начиная с кода из другого вопроса:

class MyClass {
  private static MyClass myClass = new MyClass();
  private static final Object obj = new Object();
  public MyClass() {
    System.out.println(obj); // will print null once
  }
}

Ссылка на этот класс запустит инициализацию. Сначала класс будет помечен как инициализированный. Затем первое статическое поле будет инициализировано новым экземпляром MyClass (). Обратите внимание, что myClass сразу получает ссылку на пустой экземпляр MyClass. Пробел есть, но все значения равны нулю. Конструктор теперь выполняется и печатает obj, что равно нулю.

Теперь вернемся к инициализации класса: objделается ссылка на новый реальный объект, и все готово.

Если это было установлено таким оператором, как: MyClass mc = new MyClass();пространство для нового экземпляра MyClass снова выделяется (и ссылка помещается в mc). Конструктор снова выполняется и снова печатает obj, который теперь не равен нулю.

Настоящая уловка здесь в том, что когда вы используете new, as in WhatEverItIs weii = new WhatEverItIs( p1, p2 ); weiiсразу получает ссылку на немного обнуленной памяти. Затем JVM перейдет к инициализации значений и выполнению конструктора. Но если вы каким-то образом ссылаетесь weii до этого - например, ссылаясь на него из другого потока или или путем ссылки из инициализации класса - вы смотрите на экземпляр класса, заполненный нулевыми значениями.

Ральф Чапин
источник
1
Класс не помечается как инициализированный до тех пор, пока инициализация не будет завершена - иначе делать это не имеет смысла. Пометка как инициализированная - почти последний сделанный шаг. См. JLS 12.4.2 .
Дэйв Ньютон
@DaveNewton: как только что-то ссылается на класс и начинает его инициализировать, все дальнейшие ссылки будут рассматривать класс как инициализированный. Они не будут пытаться инициализировать его, и они не будут ждать его инициализации. Следовательно, поля, которые кажутся ненулевыми с самого начала программы, могут фактически быть нулевыми какое-то время. Непонимание этого - вот что вызывает всю путаницу. Я думаю, что проще всего сказать, что неинициализированный класс «помечен» как инициализированный по первой ссылке, все остальные ссылки рассматривают его как инициализированный, и вот почему это происходит.
RalphChapin 02
Исправление к предыдущему комментарию: как описано в JLS 12.4.2 Дэйва Ньютона, класс блокируется во время инициализации. Другие потоки будут ждать инициализации класса. Однако это не влияет на этот случай, поскольку все происходит в одном потоке.
RalphChapin 02
4

Статическая переменная может быть инициализирована следующими тремя способами: выберите любой, который вам нравится.

  1. вы можете инициализировать его во время объявления
  2. или вы можете сделать статический блок, например:

    static {
            // whatever code is needed for initialization goes here
        }
    
  3. Есть альтернатива статическим блокам - вы можете написать приватный статический метод

    class name {
        public static varType myVar = initializeVar();
    
        private static varType initializeVar() {
            // initialization code goes here
        }
    }
    
Аджай Верма
источник