Почему статические поля не инициализируются во времени? - PullRequest
41 голосов
/ 30 марта 2010

Следующий код печатает null один раз.

class MyClass {
   private static MyClass myClass = new MyClass();
   private static final Object obj = new Object();
   public MyClass() {
      System.out.println(obj);
   }
   public static void main(String[] args) {}
}

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

Обновление

Я просто скопировал этот пример программы без внимания, я думал, что мы говорим о 2 полях объекта, теперь я увидел, что первое поле MyClass ..: /

Ответы [ 5 ]

37 голосов
/ 30 марта 2010

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

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

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

EDIT

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

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

Это ясно?

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

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

24 голосов
/ 01 апреля 2010

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

Это последовательность, которую 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 гарантирует, что инициализация будет завершена до того, как любой другой поток сможет использовать этот класс.

19 голосов
/ 30 марта 2010

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

  1. новый MyClass
  2. новый объект

Когда # 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
  }
}
0 голосов
/ 02 апреля 2012

@ Pyrolistical

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

нуль ноль testInitialize.MyObject@70f9f9d8 нуль

0 голосов
/ 30 марта 2010

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

...