java枚舉類型(enum)

枚舉實現原理

package com.own.learn.jdk.enum1;



public enum Day {
    MONDAY("monday"), TUESDAY("TUESDAY"), WEDNESDAY("WEDNESDAY"),
    THURSDAY("THURSDAY"), FRIDAY("FRIDAY"), SATURDAY("SATURDAY"), SUNDAY("SUNDAY");

    private String name;

    Day(String name) {
        this.name = name;
    }
}

拿出來反編譯的代碼:

javap -c com/own/learn/jdk/enum1/Day.class

public final class com.own.learn.jdk.enum1.Day extends java.lang.Enum<com.own.learn.jdk.enum1.Day> {
  public static final com.own.learn.jdk.enum1.Day MONDAY;

  public static final com.own.learn.jdk.enum1.Day TUESDAY;

  public static final com.own.learn.jdk.enum1.Day WEDNESDAY;

  public static final com.own.learn.jdk.enum1.Day THURSDAY;

  public static final com.own.learn.jdk.enum1.Day FRIDAY;

  public static final com.own.learn.jdk.enum1.Day SATURDAY;

  public static final com.own.learn.jdk.enum1.Day SUNDAY;

  public static com.own.learn.jdk.enum1.Day[] values();
    Code:
       0: getstatic     #1                  // Field $VALUES:[Lcom/own/learn/jdk/enum1/Day;
       3: invokevirtual #2                  // Method "[Lcom/own/learn/jdk/enum1/Day;".clone:()Ljava/lang/Object;
       6: checkcast     #3                  // class "[Lcom/own/learn/jdk/enum1/Day;"
       9: areturn

  public static com.own.learn.jdk.enum1.Day valueOf(java.lang.String);
    Code:
       0: ldc           #4                  // class com/own/learn/jdk/enum1/Day
       2: aload_0
       3: invokestatic  #5                  // Method java/lang/Enum.valueOf:(Ljava/lang/Class;Ljava/lang/String;)Ljava/lang/Enum;
       6: checkcast     #4                  // class com/own/learn/jdk/enum1/Day
       9: areturn

  static {};
    Code:
       0: new           #4                  // class com/own/learn/jdk/enum1/Day
       3: dup
       4: ldc           #8                  // String MONDAY
       6: iconst_0
       7: ldc           #9                  // String monday
       9: invokespecial #10                 // Method "<init>":(Ljava/lang/String;ILjava/lang/String;)V
      12: putstatic     #11                 // Field MONDAY:Lcom/own/learn/jdk/enum1/Day;
      15: new           #4                  // class com/own/learn/jdk/enum1/Day
      18: dup
      19: ldc           #12                 // String TUESDAY
      21: iconst_1
      22: ldc           #12                 // String TUESDAY
      24: invokespecial #10                 // Method "<init>":(Ljava/lang/String;ILjava/lang/String;)V
      27: putstatic     #13                 // Field TUESDAY:Lcom/own/learn/jdk/enum1/Day;
      30: new           #4                  // class com/own/learn/jdk/enum1/Day
      33: dup
      34: ldc           #14                 // String WEDNESDAY
      36: iconst_2
      37: ldc           #14                 // String WEDNESDAY
      39: invokespecial #10                 // Method "<init>":(Ljava/lang/String;ILjava/lang/String;)V
      42: putstatic     #15                 // Field WEDNESDAY:Lcom/own/learn/jdk/enum1/Day;
      45: new           #4                  // class com/own/learn/jdk/enum1/Day
      48: dup
      49: ldc           #16                 // String THURSDAY
      51: iconst_3
      52: ldc           #16                 // String THURSDAY
      54: invokespecial #10                 // Method "<init>":(Ljava/lang/String;ILjava/lang/String;)V
      57: putstatic     #17                 // Field THURSDAY:Lcom/own/learn/jdk/enum1/Day;
      60: new           #4                  // class com/own/learn/jdk/enum1/Day
      63: dup
      64: ldc           #18                 // String FRIDAY
      66: iconst_4
      67: ldc           #18                 // String FRIDAY
      69: invokespecial #10                 // Method "<init>":(Ljava/lang/String;ILjava/lang/String;)V
      72: putstatic     #19                 // Field FRIDAY:Lcom/own/learn/jdk/enum1/Day;
      75: new           #4                  // class com/own/learn/jdk/enum1/Day
      78: dup
      79: ldc           #20                 // String SATURDAY
      81: iconst_5
      82: ldc           #20                 // String SATURDAY
      84: invokespecial #10                 // Method "<init>":(Ljava/lang/String;ILjava/lang/String;)V
      87: putstatic     #21                 // Field SATURDAY:Lcom/own/learn/jdk/enum1/Day;
      90: new           #4                  // class com/own/learn/jdk/enum1/Day
      93: dup
      94: ldc           #22                 // String SUNDAY
      96: bipush        6
      98: ldc           #22                 // String SUNDAY
     100: invokespecial #10                 // Method "<init>":(Ljava/lang/String;ILjava/lang/String;)V
     103: putstatic     #23                 // Field SUNDAY:Lcom/own/learn/jdk/enum1/Day;
     106: bipush        7
     108: anewarray     #4                  // class com/own/learn/jdk/enum1/Day
     111: dup
     112: iconst_0
     113: getstatic     #11                 // Field MONDAY:Lcom/own/learn/jdk/enum1/Day;
     116: aastore
     117: dup
     118: iconst_1
     119: getstatic     #13                 // Field TUESDAY:Lcom/own/learn/jdk/enum1/Day;
     122: aastore
     123: dup
     124: iconst_2
     125: getstatic     #15                 // Field WEDNESDAY:Lcom/own/learn/jdk/enum1/Day;
     128: aastore
     129: dup
     130: iconst_3
     131: getstatic     #17                 // Field THURSDAY:Lcom/own/learn/jdk/enum1/Day;
     134: aastore
     135: dup
     136: iconst_4
     137: getstatic     #19                 // Field FRIDAY:Lcom/own/learn/jdk/enum1/Day;
     140: aastore
     141: dup
     142: iconst_5
     143: getstatic     #21                 // Field SATURDAY:Lcom/own/learn/jdk/enum1/Day;
     146: aastore
     147: dup
     148: bipush        6
     150: getstatic     #23                 // Field SUNDAY:Lcom/own/learn/jdk/enum1/Day;
     153: aastore
     154: putstatic     #1                  // Field $VALUES:[Lcom/own/learn/jdk/enum1/Day;
     157: return
}

轉換成代碼 大概是這樣的:

package com.own.learn.jdk.enum1;

public final class DayEnum extends Enum {
    public static DayEnum MONDAY;

    public static DayEnum TUESDAY;

    public static DayEnum WEDNESDAY;

    public static DayEnum THURSDAY;

    public static DayEnum FRIDAY;

    public static DayEnum SATURDAY;

    public static DayEnum SUNDAY;
    private static DayEnum $VALUES[];

    static {
        MONDAY = new DayEnum("MONDAY", 0);
        TUESDAY = new DayEnum("TUESDAY", 1);
        WEDNESDAY = new DayEnum("WEDNESDAY", 2);
        THURSDAY = new DayEnum("THURSDAY", 3);
        FRIDAY = new DayEnum("FRIDAY", 4);
        SATURDAY = new DayEnum("SATURDAY", 5);
        SUNDAY = new DayEnum("SUNDAY", 6);
        $VALUES = (new DayEnum[]{
                MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY, SUNDAY
        });
    }


    public DayEnum[] values() {
        return (DayEnum[]) $VALUES.clone();
    }

    public DayEnum valueOf(String name) {
        return (DayEnum) Enum.valueOf(com.own.learn.jdk.enum1.DayEnum.class, name);

    }

    protected DayEnum(String name, int ordinal) {
        super(name, ordinal);
    }

    @Override
    public String toString() {
        return super.toString();
    }
}

Enum類

public abstract class Enum<E extends Enum<E>>
        implements Comparable<E>, Serializable {

    private final String name;


    public final String name() {
        return name;
    }


    private final int ordinal;


    public final int ordinal() {
        return ordinal;
    }


    protected Enum(String name, int ordinal) {
        this.name = name;
        this.ordinal = ordinal;
    }


    public String toString() {
        return name;
    }


    public final boolean equals(Object other) {
        return this == other;
    }


    public final int hashCode() {
        return super.hashCode();
    }


    protected final Object clone() throws CloneNotSupportedException {
        throw new CloneNotSupportedException();
    }


    public final int compareTo(E o) {
        Enum<?> other = (Enum<?>) o;
        Enum<E> self = this;
        if (self.getClass() != other.getClass() && // optimization
                self.getDeclaringClass() != other.getDeclaringClass())
            throw new ClassCastException();
        return self.ordinal - other.ordinal;
    }


    @SuppressWarnings("unchecked")
    public final Class<E> getDeclaringClass() {
        Class<?> clazz = getClass();
        Class<?> zuper = clazz.getSuperclass();
        return (zuper == Enum.class) ? (Class<E>) clazz : (Class<E>) zuper;
    }


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

    /**
     * enum classes cannot have finalize methods.
     */
    protected final void finalize() {
    }


    private void readObject(ObjectInputStream in) throws IOException,
            ClassNotFoundException {
        throw new InvalidObjectException("can't deserialize enum");
    }

    private void readObjectNoData() throws ObjectStreamException {
        throw new InvalidObjectException("can't deserialize enum");
    }
}
  1. ordinal: 從0到Integer.Max_value的數字,只在EnumSet和EnumMap使用,默認是0.
  2. equals方法:`return this==other。枚舉是常量
  3. compareTo:默認比較的是:ordinal
  4. valueOf:enumType.enumConstantDirectory().get(name);
  Map<String, T> enumConstantDirectory() {
        if (enumConstantDirectory == null) {
            T[] universe = getEnumConstantsShared();
            if (universe == null)
                throw new IllegalArgumentException(
                    getName() + " is not an enum type");
            Map<String, T> m = new HashMap<>(2 * universe.length);
            for (T constant : universe)
                m.put(((Enum<?>)constant).name(), constant);
            enumConstantDirectory = m;
        }
        return enumConstantDirectory;
    }
 T[] getEnumConstantsShared() {
        if (enumConstants == null) {
            if (!isEnum()) return null;
            try {
                final Method values = getMethod("values");
                java.security.AccessController.doPrivileged(
                    new java.security.PrivilegedAction<Void>() {
                        public Void run() {
                                values.setAccessible(true);
                                return null;
                            }
                        });
                @SuppressWarnings("unchecked")
                T[] temporaryConstants = (T[])values.invoke(null);
                enumConstants = 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 enumConstants;
    }
    private volatile transient T[] enumConstants = null;

通過反射調用values方法得到枚舉的數組,然後轉成map

枚舉的進階用法

向enum類添加方法與自定義構造函數

public enum Day {
    MONDAY("monday"), TUESDAY("TUESDAY"), WEDNESDAY("WEDNESDAY"),
    THURSDAY("THURSDAY"), FRIDAY("FRIDAY"), SATURDAY("SATURDAY"), SUNDAY("SUNDAY");

    private String name;

    Day(String name) {
        this.name = name;
    }
}

enum類中定義抽象方法

package com.own.learn.jdk.enum1;

public enum  DayAbstractMethod {

    FOOD {
        @Override
        public void printName() {
            System.out.println(" name is " + name());
        }
    };

    public abstract void printName();


    public static void main(String[] args) {

        DayAbstractMethod.FOOD.printName();
    }
}

enum類與接口

package com.own.learn.jdk.enum1;

interface Start{
    void start();
}

interface End{
    void end();
}
public enum  EnumInterface implements Start, End{
    TEST1
    ;


    @Override
    public void start() {

    }

    @Override
    public void end() {

    }
}

與單例模式

public enum  EnumSingleton {

     private enum MyEnumSingleton{
        INSTANCE;
        private Resource resource;

        private MyEnumSingleton(){
            resource = new Resource();
        }

        public Resource getResource(){
            return resource;
        }
    }

    public static Resource getResource(){
        return MyEnumSingleton.INSTANCE.getResource();
    }

}

多線程環境下的安全性

這個特性上面已經介紹了,因爲INSTANCE最後會被編譯器處理成static final的,並且在static模塊中進行的初始化,因此它的實例化是在class被加載階段完成,是線程安全的。這個特性也決定了枚舉單例不是lazy的,如果你的單例初始化比較費時且大多數情況下只會被引用但是不會被真正調用的話,你需要使用lazy的單例模式(上面傳統單例的方法二實現),而不要選擇枚舉單例。

不可被反射實例化

在Java中,不僅通過限制enum只能聲明private的構造方法來防止Enum被使用new進行實例化,而且還限制了使用反射的方法不能通過Constructor來newInstance一個枚舉實例。

序列化的唯一性

在序列化和反序列化的時候Enum的處理是和其他類不同的,在序列化的時候實際上只寫入了Enum的name成員,而沒有保存ordinal成員;在反序列化的時候從ObjectInputStream中讀取Enum的name成員,同時調用Enum的valueof方法傳入讀取的name得到對應的ordinal值。同時Enum是不支持自定義序列化和反序列化的,一些序列化和反序列化對應的函數例如readObject和writeObject及serialVersionUID等屬性在Enum的序列化過程中都是被忽略的。
Enum在序列化和反序列化上的特性保證了使用Enum來實現單例是經受得起序列化的攻擊的。

Java枚舉單例

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章