Execution order of Enum in java

前端 未结 4 816
醉话见心
醉话见心 2021-01-12 10:14

I got a question about Enum.

I have an enum class looks like below

public enum FontStyle {
    NORMAL(\"This font has normal style.\"),
    BOLD(\"T         


        
4条回答
  •  终归单人心
    2021-01-12 10:54

    TLDR: enum values are constants created once at runtime, during the initialization phase of the enum class loading. This is efficient as the enum values are only created once.

    Long answer: Enums are not magical elements, but it takes some time to understand how they work. The enum behavior is related to the class loading process which can be summarized by 3 phases:

    • loading: the class bytecode is loaded by the classloader
    • linking: the class hierarchy is resolved (there is a subphase called resolution)
    • initializing: the class is initialized by calling the static initializer blocks

    Let's explain this using the following enum class:

    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);
        }
    }
    

    In order to understand how it works for enums, lets decompile the code using javap -c MyEnum. This will learn us that:

    1. an enum is implemented as a subclass of java.lang.Enum
    2. enum values are constants (i.e. public static final values) in the class
    3. all the enum values are created in at the beginning of static initializer block, thus they are created in the initialize phase of the loading process, so after the bytecode loading and dependencies linking phases. As they are created in the static initializer block, it is executed only once (and not every time we use the enum in a switch).
    4. MyEnum.values() returns the list of all enum values as an immutable copy of the enum values array.

    The decompiled code is the following:

    // 1. an enum is implemented as a special class
    public final class mypackage.MyEnum extends java.lang.Enum {
      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 "":(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 "":(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
    }
    

    Consequently, the enum values are created when the static initializer block is executed, that is in the initialization phase. This can be done with one of the following methods:

    • the first time an enum value is obtained (e.g. System.out.println(MyEnum.V1))
    • when executing a static method of the enum (e.g. MyEnum.valueOf() or MyEnum.myStaticMethod())
    • with Class.forName("mypackage.MyEnum") (which does the loading, linking and initializing phases)
    • when calling MyEnum.class.getEnumConstants()

    However the enum values will NOT be initialized with the following operation (which do only the loading phase, and potentially the linking phase):

    • MyEnum.class.anyMethod() (except getEnumConstants() of course): basically we access only the class metadatas, not the implementation
    • Class.forName("myPackage.MyEnum", false, aClassLoader): the false value parameter tells the classloader to avoid the initialization phase
    • ClassLoader.getSystemClassLoader().loadClass("myPackage.MyEnum"): explicitely does only the loading phase.

    Some fun other facts about enums:

    • Class.getInstance() throws an Exception: because there is no public constructor in the enum
    • the initialization blocks execution orders seems to be reversed from the usual one (first instance initializer block V1, then constructor block constructor V1, then static initializer static init): from the decompiled code, we saw that enum values initialization takes place on the beginning of the static initializer block. For each enum value, this static initializer create a new instance, which calls the instance initializer block, then the constructor block. The static initializer ends by executing the custom static initializer block.

提交回复
热议问题