Коли ініціалізуються статичні змінні?


87

Мені цікаво, коли статичні змінні ініціалізуються до значень за замовчуванням. Чи правильно, що при завантаженні класу створюються (виділяються) статичні вари, тоді виконуються статичні ініціалізатори та ініціалізації в деклараціях? У який момент даються значення за замовчуванням? Це призводить до проблеми прямого посилання.

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


2
Будь ласка, відредагуйте своє запитання, включивши цитату, на яку ви посилаєтесь.
Олівер Чарлсворт

1
Чи читали ви специфікацію мови Java? Це цілком читаний документ, свідомо. Якщо ви прочитали це, ви можете зрозуміти, що відбувається. Якщо ні, то ви можете задати більш конкретне питання як мінімум ...
Маартен Бодеус

Я думаю, що ці запитання - це копія stackoverflow.com/questions/3499214 .
Стівен С

Відповіді:


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здається друкарською помилкою. Внутрішні класи не статичні.
Даніель Любаров,

Однак слід використовувати замість Хоча
Suraj Jain

Коли ви запускаєте JVM і завантажуєте клас вперше (це робиться завантажувачем класів, коли на клас вперше вказується будь-який спосіб), будь-які статичні блоки або поля "завантажуються" в JVM і стають доступними.
nhoxbypass

1
На жаль, ця відповідь містить деякі фактичні неточності щодо того, коли статика ініціалізується. Будь ласка, див. Stackoverflow.com/a/3499322/139985 .
Стівен С

15

Подивитися:

Останній, зокрема, забезпечує детальні кроки ініціалізації, які визначають, коли ініціалізуються статичні змінні, і в якому порядку (з тим застереженням, що finalпершими ініціалізуються змінні класу та поля інтерфейсу, які є константами часу компіляції).

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


11

Статичні поля ініціалізуються, коли клас завантажується завантажувачем класу. На даний момент присвоюються значення за замовчуванням. Це робиться в порядку, ніж вони відображаються у вихідному коді.



6

статична змінна

  • Це змінна, яка належить до класу, а не до об'єкта (екземпляра)
  • Статичні змінні ініціалізуються лише один раз, на початку виконання (коли Classloader завантажує клас вперше).
  • Ці змінні будуть ініціалізовані спочатку, перед ініціалізацією будь-яких змінних екземпляра
  • Одна копія, якою мають користуватися всі екземпляри класу
  • До статичної змінної можна отримати прямий доступ за назвою класу і не потребує жодного об’єкта

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, як в WhatEverItIs weii = new WhatEverItIs( p1, p2 ); weii, одразу дається посилання на трохи обнуленої пам'яті. Потім JVM продовжить ініціалізацію значень та запуск конструктора. Але якщо ви якось посилаєтесь weii перед цим - посилаючись на нього з іншого потоку або, наприклад, посилаючись на ініціалізацію класу - ви дивитесь на екземпляр класу, заповнений нульовими значеннями.


1
Клас не позначений як ініціалізований, поки ініціалізація не буде завершена - в іншому випадку не має сенсу. Позначення як ініціалізоване - це майже останній зроблений крок. Див. JLS 12.4.2 .
Дейв Ньютон,

@DaveNewton: Як тільки щось посилається на клас і починає його ініціалізувати, усі подальші посилання будуть розглядати клас як ініціалізований. Вони не намагатимуться його ініціалізувати і не чекатимуть його ініціалізації. Отже, поля, які здаються ненульовими з моменту запуску програми, можуть фактично бути нульовими протягом певного часу. Нерозуміння цього спричинює всю плутанину. Я думаю, що найпростіше сказати, що неініціалізований клас "позначений" як ініціалізований при першому посиланні, всі інші посилання вважають його ініціалізованим, і саме тому це трапляється.
RalphChapin

Виправлення до попереднього коментаря: як описано в 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
        }
    }
    
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.