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

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

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

7 ответов

Решение
  • Это переменная, которая принадлежит классу, а не объекту (экземпляру)
  • Статические переменные инициализируются только один раз, в начале выполнения.
  • Эти переменные будут инициализированы в первую очередь, до инициализации любых переменных экземпляра
  • Одна копия для общего доступа всем экземплярам класса
  • Статическая переменная может быть доступна напрямую по имени класса и не нуждается в каком-либо объекте. См. Методы статических переменных 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 поля.

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

Увидеть:

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

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

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

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

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

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

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

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

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, как в WhatEverItIs weii = new WhatEverItIs( p1, p2 );weii немедленно дается ссылка на немного обнуляемой памяти. Затем JVM будет инициализировать значения и запускать конструктор. Но если вы как-то ссылаетесь weii прежде чем это сделать - ссылаясь на него из другого потока или, например, ссылаясь на инициализацию класса, - вы смотрите на экземпляр класса, заполненный нулевыми значениями.

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

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

    static {// любой код, необходимый для инициализации, идет сюда}

*

  1. Есть альтернатива статическим блокам - вы можете написать приватный статический метод

class name {
    public static varType myVar = initializeVar();

    private static varType initializeVar() {
        // initialization code goes here
    }
}

Другие вопросы по тегам