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

Следующий код печатает 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 ответов

Решение

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

Проверь это:

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

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

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

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

Это потому, что 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
  }
}

@Pyrolistical

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

null null testInitialize.MyObject@70f9f9d8 null

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

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