Синглтон через enum-путь ленивый инициализирован?

Это очень распространенный одноэлементный код перечисления:

public enum enumClazz{
   INSTANCE
   enumClazz(){
     //do something
   }
}

и множество мест сказали, что это ленивая инициализация. Но я смущен после того, как прочитал главу 7 " Внутри виртуальной машины Java '- Время жизни типа:

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

  • Создается новый экземпляр класса (в байт-кодах, выполнение новой инструкции. В качестве альтернативы, посредством неявного создания, отражения, клонирования или десериализации.)
  • Вызов статического метода, объявленного классом (в байт-кодах, выполнение invokestatic инструкции)
  • Использование или назначение статического поля, объявленного классом или интерфейсом, за исключением статических полей, которые являются окончательными и инициализируются выражение константы времени компиляции (в байт-кодах, выполнение getstatic или putstatic)
  • Вызов некоторых отражающих методов в Java API, таких как методы класса Class или классы в java.lang.reflect пакет
  • Инициализация подкласса класса (инициализация класса требует предварительной инициализации его суперкласса.)
  • Обозначение класса как исходного класса (с помощью метода main() <) при запуске виртуальной машины Java

Третья точка с жирным стилем уточняет, что если поле static final, инициализация поля происходит во время компиляции. Аналогично, INSTANCE в enumClazz неявно равен public static final и соответствует третьей точке.

Может ли кто-то исправить меня, если мое понимание неверно?

Ответ 1

enum поля экземпляра не инициализируются выражением постоянной времени компиляции. Oни не может быть, потому что only String и примитивные типы являются возможными типами для выражения константы времени компиляции.

Это означает, что класс будет инициализирован при первом обращении INSTANCE (что является точно желаемым эффектом).

Исключение в жирном тексте выше существует, потому что те константы (static final, инициализированные выражением константы времени компиляции) будут эффективно включены во время компиляции:

class A {
  public static final String FOO = "foo";

  static {
    System.out.println("initializing A");
  }
}

class B {
  public static void main(String[] args) {
    System.out.println(A.FOO);
  }
}

Выполнение класса B в этом примере будет не инициализировать A (и не будет печатать "инициализация A" ). И если вы посмотрите на байт-код, сгенерированный для B, вы увидите строковый литерал со значением "foo" и никакой ссылкой на класс A.

Ответ 2

Третья точка с жирным стилем уточняет, что если поле является "статическим окончанием", то инициализация поля происходит в режиме согласования

Не совсем - это относится только к "статическим полям, которые являются окончательными и инициализируются выражением постоянной времени компиляции":

static final String = "abc"; //compile time constant
static final Object = new Object(); //initialised at runtime

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

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

Class<?> c = enumClazz.class;