java枚举类

371 阅读2分钟

java枚举类:

public enum Animal {
    Dog,
    Pig,
    Fish
}

javap -p Animal.class:

public final class main.com.example.test.Animal extends java.lang.Enum<main.com.example.test.Animal> {
  public static final main.com.example.test.Animal Dog;
  public static final main.com.example.test.Animal Pig;
  public static final main.com.example.test.Animal Fish;
  private static final main.com.example.test.Animal[] $VALUES;
  public static main.com.example.test.Animal[] values();
  public static main.com.example.test.Animal valueOf(java.lang.String);
  private main.com.example.test.Animal();
  static {};
}
  1. Animal继承Enum
  2. 每个枚举值都是静态变量

javap -c Animal.class

public final class main.com.example.test.Animal extends java.lang.Enum<main.com.example.test.Animal> {
  public static final main.com.example.test.Animal Dog;

  public static final main.com.example.test.Animal Pig;

  public static final main.com.example.test.Animal Fish;

  public static main.com.example.test.Animal[] values();
    Code:
       0: getstatic     #1                  // Field $VALUES:[Lmain/com/example/test/Animal;
       3: invokevirtual #2                  // Method "[Lmain/com/example/test/Animal;".clone:()Ljava/lang/Object;
       6: checkcast     #3                  // class "[Lmain/com/example/test/Animal;"
       9: areturn

  public static main.com.example.test.Animal valueOf(java.lang.String);
    Code:
       0: ldc           #4                  // class main/com/example/test/Animal
       2: aload_0
       3: invokestatic  #5                  // Method java/lang/Enum.valueOf:(Ljava/lang/Class;Ljava/lang/String;)Ljava/lang/Enum;
       6: checkcast     #4                  // class main/com/example/test/Animal
       9: areturn

  static {};
    Code:
       0: new           #4                  // class main/com/example/test/Animal
       3: dup
       4: ldc           #7                  // String Dog
       6: iconst_0
       7: invokespecial #8                  // Method "<init>":(Ljava/lang/String;I)V
      10: putstatic     #9                  // Field Dog:Lmain/com/example/test/Animal;
      13: new           #4                  // class main/com/example/test/Animal
      16: dup
      17: ldc           #10                 // String Pig
      19: iconst_1
      20: invokespecial #8                  // Method "<init>":(Ljava/lang/String;I)V
      23: putstatic     #11                 // Field Pig:Lmain/com/example/test/Animal;
      26: new           #4                  // class main/com/example/test/Animal
      29: dup
      30: ldc           #12                 // String Fish
      32: iconst_2
      33: invokespecial #8                  // Method "<init>":(Ljava/lang/String;I)V
      36: putstatic     #13                 // Field Fish:Lmain/com/example/test/Animal;
      39: iconst_3
      40: anewarray     #4                  // class main/com/example/test/Animal
      43: dup
      44: iconst_0
      45: getstatic     #9                  // Field Dog:Lmain/com/example/test/Animal;
      48: aastore
      49: dup
      50: iconst_1
      51: getstatic     #11                 // Field Pig:Lmain/com/example/test/Animal;
      54: aastore
      55: dup
      56: iconst_2
      57: getstatic     #13                 // Field Fish:Lmain/com/example/test/Animal;
      60: aastore
      61: putstatic     #1                  // Field $VALUES:[Lmain/com/example/test/Animal;
      64: return
}
  1. 在static代码块中会对每个枚举值进行初始化

  2. $VALUES是一个静态变量,是枚举值数组,在static代码块中进行初始化和添加

  3. values()方法返回的就是$VALUES数组的拷贝,浅拷贝

  4. valuesOf()方法根据字符串返回对应的枚举值,调用了Enum.valueOf()方法:

    //Enum.java
    public static <T extends Enum<T>> T valueOf(Class<T> enumType,
                                                    String name) {
        T result = enumType.enumConstantDirectory().get(name);
        if (result != null)
            return result;
        if (name == null)
            throw new NullPointerException("Name is null");
        throw new IllegalArgumentException(
            "No enum constant " + enumType.getCanonicalName() + "." + name);
    }
    

    enumConstantDirectory的来源可以看Class的源码

    Map<String, T> enumConstantDirectory() {
        Map<String, T> directory = enumConstantDirectory;
        if (directory == null) {
            T[] universe = getEnumConstantsShared();
            if (universe == null)
                throw new IllegalArgumentException(
                    getName() + " is not an enum type");
            directory = new HashMap<>((int)(universe.length / 0.75f) + 1);
            for (T constant : universe) {
                directory.put(((Enum<?>)constant).name(), constant);
            }
            enumConstantDirectory = directory;
        }
        return directory;
    }
    

    getEnumConstantsShared()方法也在Class.java中:

    T[] getEnumConstantsShared() {
        T[] constants = enumConstants;
        if (constants == null) {
            if (!isEnum()) return null;
            try {
                final Method values = getMethod("values");
                java.security.AccessController.doPrivileged(
                    new java.security.PrivilegedAction<>() {
                        public Void run() {
                                values.setAccessible(true);
                                return null;
                            }
                        });
                @SuppressWarnings("unchecked")
                T[] temporaryConstants = (T[])values.invoke(null);
                enumConstants = constants = temporaryConstants;
            }
            // These can happen when users concoct enum-like classes
            // that don't comply with the enum spec.
            catch (InvocationTargetException | NoSuchMethodException |
                   IllegalAccessException ex) { return null; }
        }
        return constants;
    }
    

    所以可以看到是通过反射调用枚举类中的values()方法得到枚举值的数组,然后生成一个HashMap映射表