Синглтон через enum way ленивый инициализируется?

Это очень распространенный синглтон-код enum:

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

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

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

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

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

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

2 ответа

Решение

enum Поля экземпляра не "инициализируются константным выражением во время компиляции". Они не могут быть, потому что только 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,

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

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

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

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

Так что это действительно лениво, если, конечно, у вас нет кода где-то еще в вашем коде, который использует перечисление.

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