Синглтон через 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
ссылка в вашем коде.
Так что это действительно лениво, если, конечно, у вас нет кода где-то еще в вашем коде, который использует перечисление.