Jun*_* Oh 6 java enums runtime compilation initialization
我有一个关于Enum的问题.
我有一个enum类,如下所示
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;
}
}
Run Code Online (Sandbox Code Playgroud)
我想知道这个Enum对象何时被创建.
枚举看起来像'静态最终'对象,因为它的值永远不会改变.因此,在此目的中,仅在编译时初始化是有效的.
但它在顶层调用自己的构造函数,所以我怀疑它可以在我们调用它时生成,例如,在switch语句中.
是的,枚举是静态常量,但不是编译时常量.就像任何其他类一样,在第一次需要时加载枚举.如果稍微改变它的构造函数,你可以很容易地观察它
FontStyle(String description) {
System.out.println("creating instace of "+this);// add this
this.description = description;
}
Run Code Online (Sandbox Code Playgroud)
并使用简单的测试代码
class Main {
public static void main(String[] Args) throws Exception {
System.out.println("before enum");
FontStyle style1 = FontStyle.BOLD;
FontStyle style2 = FontStyle.ITALIC;
}
}
Run Code Online (Sandbox Code Playgroud)
如果您将运行main方法,您将看到输出
before enum
creating instace of NORMAL
creating instace of BOLD
creating instace of ITALIC
creating instace of UNDERLINE
Run Code Online (Sandbox Code Playgroud)
这表明当我们想第一次使用枚举时,enum类已被加载(并且其静态字段已被初始化).
你也可以使用
Class.forName("full.packag.name.of.FontStyle");
Run Code Online (Sandbox Code Playgroud)
如果尚未加载则导致其加载.
TLDR:枚举值是在枚举类加载的初始化阶段在运行时创建的常量。这是有效的,因为枚举值仅创建一次。
长答案: 枚举不是神奇的元素,但是要花些时间才能理解它们是如何工作的。枚举行为与类加载过程有关,可以分为三个阶段:
让我们使用以下枚举类对此进行解释:
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);
}
}
Run Code Online (Sandbox Code Playgroud)
为了了解它如何用于枚举,让我们使用来反编译代码javap -c MyEnum。这将使我们了解:
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
}
Run Code Online (Sandbox Code Playgroud)
因此,在执行静态初始化程序块时,即在初始化阶段,将创建枚举值。可以使用以下方法之一完成此操作:
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):falsevalue参数告诉类加载器避免初始化阶段ClassLoader.getSystemClassLoader().loadClass("myPackage.MyEnum"):明确地仅执行加载阶段。关于枚举的一些有趣的事实:
Class<MyEnum>.getInstance() 引发异常:因为枚举中没有公共构造函数block V1,然后构造块constructor V1,那么静态初始化static init):从反编译的代码中,我们看到枚举值初始化发生在静态初始化块的开始。对于每个枚举值,此静态初始化程序创建一个新实例,该实例先调用实例初始化程序块,然后再调用构造函数块。静态初始化程序通过执行定制的静态初始化程序块结束。