Почему статические поля не инициализируются во времени?

Здесь много правильных ответов, но я хотел добавить это (для полноты):

Если вы в нижней части файла cpp реализации выполняете явное инстанцирование всех типов, которые будут использоваться шаблоном с, компоновщик сможет найти их как обычно.

Изменить: добавление примера явного создания экземпляра шаблона. Используется после того, как шаблон определен, и определены все функции-члены.

template class vector<int>;

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

Вышеприведенный пример бесполезен, поскольку вектор полностью определен в заголовках, за исключением случаев, когда common include file (precompiled header?) использует extern template class vector<int>, чтобы не создавать его из всех других (1000?) файлов, которые используют вектор.

41
задан Tom Brito 6 January 2017 в 14:11
поделиться

5 ответов

Поскольку статика инициализируется в порядке, указанном в исходном коде.

Проверьте это:

class MyClass {
  private static MyClass myClass = new MyClass();
  private static MyClass myClass2 = new MyClass();
  public MyClass() {
    System.out.println(myClass);
    System.out.println(myClass2);
  }
}

Это напечатает:

null
null
myClassObject
null

РЕДАКТИРОВАТЬ

Хорошо, давайте нарисуем это, чтобы быть немного яснее.

  1. Статика инициализируется одна за другой в порядке, заявленном в исходном коде.
  2. Поскольку первый статический объект инициализируется раньше остальных, во время его инициализации остальные статические поля имеют нулевые значения или значения по умолчанию.
  3. Во время инициирования второй статики первая статика верна, но остальные все еще нулевые или по умолчанию.

Это ясно?

РЕДАКТИРОВАТЬ 2

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

37
ответ дан Gray 6 January 2017 в 14:11
поделиться

Давайте попробуем по-другому объяснить это ...

Это последовательность, которую JVM проходит, когда вы впервые ссылаетесь на класс MyClass.

  1. Загрузить байт-код в память.
  2. Память для статического хранения очищена (двоичный ноль).
  3. Инициализировать класс:
    1. Выполнить каждый статический инициализатор в том порядке, в котором он появляется, включая статические переменные и блоки static { ... }.
    2. Затем JVM инициализирует вашу статическую переменную myClass новым экземпляром MyClass.
    3. Когда это происходит, JVM замечает, что MyClass уже загружен (байт-код) и находится в процессе инициализации , поэтому пропускает инициализацию.
    4. Выделить память в куче для объекта.
    5. Выполнить конструктор.
    6. Выведите значение obj, которое все еще равно null (поскольку оно не является частью инициализированных переменных кучи и конструктора).
    7. Когда конструктор завершит работу, выполните следующий статический инициализатор, который устанавливает obj в новый экземпляр Object.
  4. Инициализация класса выполнена. С этого момента все вызовы конструктора будут вести себя так, как вы предполагаете / ожидаете, то есть obj будет не null, а ссылкой на экземпляр Object.

Помните, что Java указывает, что переменной final присваивается значение один раз. Дело не в том, что ему гарантируется присвоение значения, когда код ссылается на него, если только вы не убедитесь, что код ссылается на него после того, как оно назначено.

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

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

24
ответ дан Kevin Brock 6 January 2017 в 14:11
поделиться

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

  1. new MyClass
  2. new Object

Когда выполняется # 1, объект obj все еще не инициализирован, поэтому он печатает ноль. Попробуйте следующее, и вы увидите разницу:

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

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

class MyClass {

  private static final MyClass myClass = new MyClass();

  private Object obj = new Object();

  private MyClass() {
    System.out.println(obj); // will print null once
  }
}
19
ответ дан Slava Imeshev 6 January 2017 в 14:11
поделиться

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

0
ответ дан antony 6 January 2017 в 14:11
поделиться

@Pyrolistical

, поскольку начальная часть первого статического поля myclass не полностью построена ... полученный результат -

null null testInitialize.MyObject@70f9f9d8 null

0
ответ дан iamx7777777 6 January 2017 в 14:11
поделиться
Другие вопросы по тегам:

Похожие вопросы: