Порядок выполнения перечисления в java
У меня вопрос о перечислении.
У меня есть класс перечисления, как показано ниже
public enum FontStyle {
NORMAL("This font has normal style."),
BOLD("This font has bold style."),
ITALIC("This font has italic style."),
UNDERLINE("This font has underline style.");
private String description;
FontStyle(String description) {
this.description = description;
}
public String getDescription() {
return this.description;
}
}
интересно, когда этот объект перечисления создается.
перечисление выглядит как "статический конечный" объект, так как его значение никогда не изменится. Поэтому в этой цели эффективно инициализировать только во время компиляции.
но он вызывает свой собственный конструктор в top, поэтому я сомневаюсь, что он может генерировать всякий раз, когда мы его вызываем, например, в инструкции switch.
4 ответов
да, перечисления являются статическими константами, но не являются константами времени компиляции. Как и любые другие классы, перечисление загружается при первой необходимости. Вы можете легко наблюдать за ним, если немного измените его конструктор
FontStyle(String description) {
System.out.println("creating instace of "+this);// add this
this.description = description;
}
и использовать простой тестовый код типа
class Main {
public static void main(String[] Args) throws Exception {
System.out.println("before enum");
FontStyle style1 = FontStyle.BOLD;
FontStyle style2 = FontStyle.ITALIC;
}
}
если вы используете main
метод вы увидите выход
before enum
creating instace of NORMAL
creating instace of BOLD
creating instace of ITALIC
creating instace of UNDERLINE
который показывает, что класс enum был загружен (и его статические поля были инициализированы), когда мы хотели использовать enum первым время.
вы также можете использовать
Class.forName("full.packag.name.of.FontStyle");
чтобы вызвать его нагрузку, если он еще не был загружен.
TLDR: значения перечисления являются константами, созданными один раз во время выполнения, во время инициализации фазы загрузки класса enum. Это эффективно, так как значения перечисления создаются только один раз.
ответ: Перечисления не являются магическими элементами, но требуется некоторое время, чтобы понять, как они работают. Поведение перечисления связано с процесс загрузки Class который можно суммировать 3 фазы:
- загрузка: байт-код класса загружается classloader
- связь: иерархия классов разрешена (существует подфаза разрешение)
- инициализации: класс инициализируется вызовом статических блоков инициализатора
поясним это, используя следующий класс enum:
package mypackage;
public enum MyEnum {
V1, V2;
private MyEnum() {
System.out.println("constructor "+this);
}
static {
System.out.println("static init");
}
{
System.out.println("block "+this);
}
}
для того, чтобы понять как это работает для перечислений, позволяет декомпилировать код с помощью javap -c MyEnum
. Это научит нас, что:
- перечисление реализовано как подкласс java.ленг.Перечислимые
- значений enum-константы (т. е.
public static final
значения) в классе - все значения перечисления создаются в начале блока статического инициализатора, таким образом, они создаются в инициализации фаза процесса загрузки, поэтому после байт-кода загрузка и зависимости связь фазы. Поскольку они создаются в статическом блоке инициализатора, он выполняется только один раз (и не каждый раз, когда мы используем перечисление в коммутаторе).
-
MyEnum.values()
возвращает список всех значений перечисления в виде неизменяемой копии массива значений перечисления.
декомпилированный код следующий:
// 1. an enum is implemented as a special class
public final class mypackage.MyEnum extends java.lang.Enum<mypackage.MyEnum> {
public static final mypackage.MyEnum V1; // 2. enum values are constants of the enum class
public static final mypackage.MyEnum V2;
static {};
Code: // 3. all enum values are created in the static initializer block
// create the enum value V1
0: new #1 // class mypackage/MyEnum
3: dup
4: ldc #14 // String V1
6: iconst_0
7: invokespecial #15 // Method "<init>":(Ljava/lang/String;I)V
10: putstatic #19 // Field V1:Lmypackage/MyEnum;
// create the enum value V2
13: new #1 // class mypackage/MyEnum
16: dup
17: ldc #21 // String V2
19: iconst_1
20: invokespecial #15 // Method "<init>":(Ljava/lang/String;I)V
23: putstatic #22 // Field V2:Lmypackage/MyEnum;
// create an array to store all enum values
39: iconst_2
40: anewarray #1 // class mypackage/MyEnum
43: dup
44: iconst_0
45: getstatic #19 // Field V1:Lmypackage/MyEnum;
48: aastore
49: dup
50: iconst_1
51: getstatic #22 // Field V2:Lmypackage/MyEnum;
54: aastore
61: putstatic #27 // Field ENUM$VALUES:[Lmypackage/MyEnum;
64: getstatic #29 // Field java/lang/System.out:Ljava/io/PrintStream;
67: ldc #35 // String "static init"
69: invokevirtual #37 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
72: return
public static mypackage.MyEnum[] values();
Code: // 4. it returns an immutable copy of the field ENUM$VALUES
0: getstatic #27 // Field ENUM$VALUES:[Lmypackage/MyEnum;
3: dup
4: astore_0
5: iconst_0
6: aload_0
7: arraylength
8: dup
9: istore_1
10: anewarray #1 // class mypackage/MyEnum
13: dup
14: astore_2
15: iconst_0
16: iload_1
17: invokestatic #67 // Method java/lang/System.arraycopy:(Ljava/lang/Object;ILjava/lang/Object;II)V (=immutable copy)
20: aload_2
21: areturn
public static mypackage.MyEnum valueOf(java.lang.String);
Code:
0: ldc #1 // class mypackage/MyEnum
2: aload_0
3: invokestatic #73 // Method java/lang/Enum.valueOf:(Ljava/lang/Class;Ljava/lang/String;)Ljava/lang/Enum;
6: checkcast #1 // class mypackage/MyEnum
9: areturn
}
следовательно, значения перечисления создаются при выполнении блока статического инициализатора, то есть в инициализации этап. Это можно сделать одним из следующих методов:
- при первом получении значения перечисления (например,
System.out.println(MyEnum.V1)
) - при выполнении статического метода перечисления (например,
MyEnum.valueOf()
илиMyEnum.myStaticMethod()
) - С
Class.forName("mypackage.MyEnum")
(что означает загрузка, связь и инициализации фазы) - при вызове
MyEnum.class.getEnumConstants()
-
MyEnum.class.anyMethod()
(кромеgetEnumConstants()
конечно): в основном мы получаем доступ только метаданные класса, а не выполнение -
Class.forName("myPackage.MyEnum", false, aClassLoader)
: thefalse
параметр value указывает загрузчику классов избегать инициализации этап -
ClassLoader.getSystemClassLoader().loadClass("myPackage.MyEnum")
: явно не только загрузка этап.
некоторые забавные другие факты о перечислениях:
-
Class<MyEnum>.getInstance()
вызывает исключение: потому что в перечислении - the инициализация блоков выполнения заказов, кажется, отменяется от обычного (первый инициализатор экземпляра
block V1
, затем конструктор blockconstructor V1
, тогда статический инициализаторstatic init
): из декомпилированного кода мы увидели, что значения перечисления инициализация происходит в начале статического блока инициализатора. Для каждого значения перечисления этот статический инициализатор создает новый экземпляр, который вызывает блок инициализатора экземпляра, а затем блок конструктора. Статический инициализатор завершается выполнением пользовательского блока статического инициализатора.
экземпляры перечисления создаются только один раз при загрузке самого класса перечисления.
очень важно, чтобы они создавались только один раз, чтобы сравнение идентификаторов объектов работало (==
). Даже механизм сериализации объекта (de) должен был быть скорректирован для поддержки этого.
экземпляры перечисления создаются во время связывание классов (разрешение), который является этапом, который приходит после загрузка класс, как и статические поля "нормального" класса.
связывание классов происходит отдельно от загрузки классов. Поэтому, если вы динамически загружаете класс Enum с помощью загрузчика классов, константы будут созданы только при попытке доступа к одному из экземпляров, например, при использовании метода getEnumConstants()
от Class
.
вот немного кода для проверки приведенного выше утверждения:
File1:TestEnum.java
public enum TestEnum {
CONST1, CONST2, CONST3;
TestEnum() {
System.out.println( "Initializing a constant" );
}
}
File2: