Java枚舉類型的介紹和使用

最近在外面實(shí)習(xí),發(fā)現(xiàn)公司代碼很多地方都用到了枚舉類型,而以前很少有機(jī)會(huì)能用到它,所以特地深入地學(xué)習(xí)一下Java中的枚舉類型并做個(gè)記錄。

1. 枚舉類型的介紹

枚舉類型是一種特殊的數(shù)據(jù)類型,它使得變量成為一組預(yù)定義常量。所以在需要表示一組固定常量時(shí)應(yīng)盡量使用枚舉類型。通過關(guān)鍵字enum來定義枚舉類,它和普通類一樣可以有構(gòu)造器、成員變量、方法。

1.1 枚舉類的特性

  • 所有的枚舉類都隱式的繼承java.lang.Enum,Java不允許多繼承,所以枚舉類不能再繼承其他任何類,但可以實(shí)現(xiàn)接口
  • 枚舉類被隱式地聲明為final,所以也不能被其他任何類繼承
  • 枚舉類型的構(gòu)造函數(shù)修飾符必須是private。定義枚舉常量時(shí)會(huì)自動(dòng)調(diào)用,不能自己調(diào)用枚舉的構(gòu)造函數(shù)
  • 枚舉類的實(shí)例必須在第一行列出,并且枚舉值默認(rèn)被public static final修飾
  • 編譯時(shí)編譯器會(huì)自動(dòng)幫我們添加兩個(gè)靜態(tài)方法values()valueOf()

1.2 枚舉類的原理

下面我們定義了一個(gè)枚舉類并讓它實(shí)現(xiàn)Info接口,這樣可以讓枚舉值提供不同的實(shí)現(xiàn),當(dāng)然也可以在枚舉類里面定義一個(gè)抽象方法,這樣枚舉值也必須實(shí)現(xiàn)此抽象方法才可,效果都一樣。

public enum Season implements Info{
    SPRING("spring","春暖花開"){
        public void show(){
            System.out.println("春天在哪里");
        }
    },
    SUMMER("summer","夏日炎炎"){
        public void show(){
            System.out.println("生如夏花");
        }
    },
    AUTUMN("autumn","秋高氣爽"){
        public void show(){
            System.out.println("秋");
        }
    },
    WINTER("winter","白雪茫茫"){
        public void show(){
            System.out.println("冷");
        }
    };
    private final String seasonName;
    private final String seasonDesc;


    Season(String seasonName,String seasonDesc){
        this.seasonName = seasonName;
        this.seasonDesc = seasonDesc;
    }

    public String getSeasonName() {
        return seasonName;
    }

    public String getSeasonDesc() {
        return seasonDesc;
    }

    @Override
    public String toString() {
        return "Season{" +
                "seasonName='" + seasonName + '\'' +
                ", seasonDesc='" + seasonDesc + '\'' +
                '}';
    }

}

可以看到該枚舉類編譯后的class文件,其中還包括了四個(gè)枚舉值對(duì)應(yīng)的class文件,而且其后還帶有序號(hào)。在枚舉實(shí)例創(chuàng)建時(shí)會(huì)給每個(gè)枚舉值指定一個(gè)整形常量值(序號(hào)),若沒有顯示指定,則 整形常量值從0開始遞增。這其實(shí)是與父類Enum有關(guān),后面會(huì)介紹。

下面是利用javap工具查看Season.java經(jīng)過編譯后的字節(jié)碼(請(qǐng)忽略亂碼--.--):

"C:\Program Files\Java\jdk1.8.0_201\bin\javap.exe" -c pre.chl.enums.Season
Compiled from "Season.java"
public abstract class pre.chl.enums.Season extends java.lang.Enum<pre.chl.enums.Season> implements pre.chl.enums.Info {
  public static final pre.chl.enums.Season SPRING;

  public static final pre.chl.enums.Season SUMMER;

  public static final pre.chl.enums.Season AUTUMN;

  public static final pre.chl.enums.Season WINTER;

  public static pre.chl.enums.Season[] values();
    Code:
       0: getstatic     #2                  // Field $VALUES:[Lpre/chl/enums/Season;
       3: invokevirtual #3                  // Method "[Lpre/chl/enums/Season;".clone:()Ljava/lang/Object;
       6: checkcast     #4                  // class "[Lpre/chl/enums/Season;"
       9: areturn

  public static pre.chl.enums.Season valueOf(java.lang.String);
    Code:
       0: ldc           #5                  // class pre/chl/enums/Season
       2: aload_0
       3: invokestatic  #6                  // Method java/lang/Enum.valueOf:(Ljava/lang/Class;Ljava/lang/String;)Ljava/lang/Enum;
       6: checkcast     #5                  // class pre/chl/enums/Season
       9: areturn

  public java.lang.String getSeasonName();
    Code:
       0: aload_0
       1: getfield      #8                  // Field seasonName:Ljava/lang/String;
       4: areturn

  public java.lang.String getSeasonDesc();
    Code:
       0: aload_0
       1: getfield      #9                  // Field seasonDesc:Ljava/lang/String;
       4: areturn

  public java.lang.String toString();
    Code:
       0: new           #10                 // class java/lang/StringBuilder
       3: dup
       4: invokespecial #11                 // Method java/lang/StringBuilder."<init>":()V
       7: ldc           #12                 // String Season{seasonName='
       9: invokevirtual #13                 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
      12: aload_0
      13: getfield      #8                  // Field seasonName:Ljava/lang/String;
      16: invokevirtual #13                 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
      19: bipush        39
      21: invokevirtual #14                 // Method java/lang/StringBuilder.append:(C)Ljava/lang/StringBuilder;
      24: ldc           #15                 // String , seasonDesc='
      26: invokevirtual #13                 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
      29: aload_0
      30: getfield      #9                  // Field seasonDesc:Ljava/lang/String;
      33: invokevirtual #13                 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
      36: bipush        39
      38: invokevirtual #14                 // Method java/lang/StringBuilder.append:(C)Ljava/lang/StringBuilder;
      41: bipush        125
      43: invokevirtual #14                 // Method java/lang/StringBuilder.append:(C)Ljava/lang/StringBuilder;
      46: invokevirtual #16                 // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
      49: areturn

  pre.chl.enums.Season(java.lang.String, int, java.lang.String, java.lang.String, pre.chl.enums.Season$1);
    Code:
       0: aload_0
       1: aload_1
       2: iload_2
       3: aload_3
       4: aload         4
       6: invokespecial #1                  // Method "<init>":(Ljava/lang/String;ILjava/lang/String;Ljava/lang/String;)V
       9: return

  static {};
    Code:
       0: new           #17                 // class pre/chl/enums/Season$1
       3: dup
       4: ldc           #18                 // String SPRING
       6: iconst_0
       7: ldc           #19                 // String spring
       9: ldc           #20                 // String ???????
      11: invokespecial #21                 // Method pre/chl/enums/Season$1."<init>":(Ljava/lang/String;ILjava/lang/String;Ljava/lang/String;)V
      14: putstatic     #22                 // Field SPRING:Lpre/chl/enums/Season;
      17: new           #23                 // class pre/chl/enums/Season$2
      20: dup
      21: ldc           #24                 // String SUMMER
      23: iconst_1
      24: ldc           #25                 // String summer
      26: ldc           #26                 // String ????????
      28: invokespecial #27                 // Method pre/chl/enums/Season$2."<init>":(Ljava/lang/String;ILjava/lang/String;Ljava/lang/String;)V
      31: putstatic     #28                 // Field SUMMER:Lpre/chl/enums/Season;
      34: new           #29                 // class pre/chl/enums/Season$3
      37: dup
      38: ldc           #30                 // String AUTUMN
      40: iconst_2
      41: ldc           #31                 // String autumn
      43: ldc           #32                 // String ??????
      45: invokespecial #33                 // Method pre/chl/enums/Season$3."<init>":(Ljava/lang/String;ILjava/lang/String;Ljava/lang/String;)V
      48: putstatic     #34                 // Field AUTUMN:Lpre/chl/enums/Season;
      51: new           #35                 // class pre/chl/enums/Season$4
      54: dup
      55: ldc           #36                 // String WINTER
      57: iconst_3
      58: ldc           #37                 // String winter
      60: ldc           #38                 // String ?????
      62: invokespecial #39                 // Method pre/chl/enums/Season$4."<init>":(Ljava/lang/String;ILjava/lang/String;Ljava/lang/String;)V
      65: putstatic     #40                 // Field WINTER:Lpre/chl/enums/Season;
      68: iconst_4
      69: anewarray     #5                  // class pre/chl/enums/Season
      72: dup
      73: iconst_0
      74: getstatic     #22                 // Field SPRING:Lpre/chl/enums/Season;
      77: aastore
      78: dup
      79: iconst_1
      80: getstatic     #28                 // Field SUMMER:Lpre/chl/enums/Season;
      83: aastore
      84: dup
      85: iconst_2
      86: getstatic     #34                 // Field AUTUMN:Lpre/chl/enums/Season;
      89: aastore
      90: dup
      91: iconst_3
      92: getstatic     #40                 // Field WINTER:Lpre/chl/enums/Season;
      95: aastore
      96: putstatic     #2                  // Field $VALUES:[Lpre/chl/enums/Season;
      99: return
}

Process finished with exit code 0

可以發(fā)現(xiàn),一個(gè)枚舉類在經(jīng)過編譯后變成了一個(gè)抽象類,同時(shí)添加了兩個(gè)方法values()valueOf(String)。這里還可以看到字節(jié)碼中的構(gòu)造函數(shù)多了兩個(gè)參數(shù),一個(gè)int型一個(gè)String型,這兩個(gè)參數(shù)其實(shí)是Enum里面的。

1.3 java.lang.Enum

下面是JDK1.8的實(shí)現(xiàn),注釋做了部分翻譯。


package java.lang;

import java.io.Serializable;
import java.io.IOException;
import java.io.InvalidObjectException;
import java.io.ObjectInputStream;
import java.io.ObjectStreamException;

/**
 * 這是所有Java語言枚舉類型的公共基類。
 *
 * 有關(guān)枚舉的更多信息,包括對(duì)枚舉的描述、隱式聲明由編譯器合成的方法,
 * 可以見于<cite> Java&trade;Language</ cite>的第8、9節(jié)。
 * 另外,使用枚舉類型作為set的類型或作為Map中keys的類型時(shí),java.util.EnumSet
 * 和java.util.EnumMap更加高效
 *
 * @param <E> The enum type subclass
 * @author  Josh Bloch
 * @author  Neal Gafter
 * @see     Class#getEnumConstants()
 * @see     java.util.EnumSet
 * @see     java.util.EnumMap
 * @since   1.5
 */
public abstract class Enum<E extends Enum<E>>
        implements Comparable<E>, Serializable {
    /**
     * 枚舉常量的名稱,和枚舉聲明中聲明的一致。
     * 大多數(shù)程序員應(yīng)該使用{@link #toString}方法而不是直接訪問此字段。
     */
    private final String name;

    /**
     * Returns the name of this enum constant, exactly as declared in its
     * enum declaration.
     *
     * <b>Most programmers should use the {@link #toString} method in
     * preference to this one, as the toString method may return
     * a more user-friendly name.</b>  This method is designed primarily for
     * use in specialized situations where correctness depends on getting the
     * exact name, which will not vary from release to release.
     *
     * @return the name of this enum constant
     */
    public final String name() {
        return name;
    }

    /**
     * 這個(gè)枚舉常量的序數(shù)(它在枚舉聲明中的位置,分配給初始常量的序數(shù)為零)。
     * 大多數(shù)程序員對(duì)此字段沒有使用。它是專門設(shè)計(jì)的供復(fù)雜的基于枚舉的數(shù)據(jù)結(jié)構(gòu)使用     * 的,例如{@link java.util.EnumSet}和{@link java.util.EnumMap}.
     */
    private final int ordinal;

    /**
     * Returns the ordinal of this enumeration constant (its position
     * in its enum declaration, where the initial constant is assigned
     * an ordinal of zero).
     *
     * Most programmers will have no use for this method.  It is
     * designed for use by sophisticated enum-based data structures, such
     * as {@link java.util.EnumSet} and {@link java.util.EnumMap}.
     *
     * @return the ordinal of this enumeration constant
     */
    public final int ordinal() {
        return ordinal;
    }

    /**
     * 唯一的構(gòu)造函數(shù),程序員無法調(diào)用次構(gòu)造函數(shù)。它由編譯器在進(jìn)行枚舉類型聲明時(shí)調(diào)用。
     *
     * @param name - The name of this enum constant, which is the identifier
     *               used to declare it.
     * @param ordinal - The ordinal of this enumeration constant (its position
     *         in the enum declaration, where the initial constant is assigned
     *         an ordinal of zero).
     */
    protected Enum(String name, int ordinal) {
        this.name = name;
        this.ordinal = ordinal;
    }

    /**
     * Returns the name of this enum constant, as contained in the
     * declaration.  This method may be overridden, though it typically
     * isn't necessary or desirable.  An enum type should override this
     * method when a more "programmer-friendly" string form exists.
     *
     * @return the name of this enum constant
     */
    public String toString() {
        return name;
    }

    /**
     * Returns true if the specified object is equal to this
     * enum constant.
     *
     * @param other the object to be compared for equality with this object.
     * @return  true if the specified object is equal to this
     *          enum constant.
     */
    public final boolean equals(Object other) {
        return this==other;
    }

    /**
     * Returns a hash code for this enum constant.
     *
     * @return a hash code for this enum constant.
     */
    public final int hashCode() {
        return super.hashCode();
    }

    /**
     * 拋出CloneNotSupportedException。這保證了枚舉永遠(yuǎn)不會(huì)被克隆,這保證它們的“單例狀態(tài)”。
     *
     * @return (never returns)
     */
    protected final Object clone() throws CloneNotSupportedException {
        throw new CloneNotSupportedException();
    }

    /**
     * Compares this enum with the specified object for order.  Returns a
     * negative integer, zero, or a positive integer as this object is less
     * than, equal to, or greater than the specified object.
     *
     * Enum constants are only comparable to other enum constants of the
     * same enum type.  The natural order implemented by this
     * method is the order in which the constants are declared.
     */
    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;
    }

    /**
     * Returns the Class object corresponding to this enum constant's
     * enum type.  Two enum constants e1 and  e2 are of the
     * same enum type if and only if
     *   e1.getDeclaringClass() == e2.getDeclaringClass().
     * (The value returned by this method may differ from the one returned
     * by the {@link Object#getClass} method for enum constants with
     * constant-specific class bodies.)
     *
     * @return the Class object corresponding to this enum constant's
     *     enum type
     */
    @SuppressWarnings("unchecked")
    public final Class<E> getDeclaringClass() {
        Class<?> clazz = getClass();
        Class<?> zuper = clazz.getSuperclass();
        return (zuper == Enum.class) ? (Class<E>)clazz : (Class<E>)zuper;
    }

    /**
     * 返回指定枚舉類型和名稱的枚舉常量,名稱必須與聲明的完全匹配。
     * 無關(guān)的空白字符是不被允許的。
     * <p>Note that for a particular enum type {@code T}, the
     * implicitly declared {@code public static T valueOf(String)}
     * method on that enum may be used instead of this method to map
     * from a name to the corresponding enum constant. 所有的枚舉常量值可以通過調(diào)用隱式方法{@code public static T[] values()}獲得。
     *
     * @param <T> The enum type whose constant is to be returned
     * @param enumType the {@code Class} object of the enum type from which
     *      to return a constant
     * @param name the name of the constant to return
     * @return the enum constant of the specified enum type with the
     *      specified name
     * @throws IllegalArgumentException if the specified enum type has
     *         no constant with the specified name, or the specified
     *         class object does not represent an enum type
     * @throws NullPointerException if {@code enumType} or {@code name}
     *         is null
     * @since 1.5
     */
    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() { }

    /**
     * 防止默認(rèn)反序列化。
     */
    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");
    }
}

引用API文檔對(duì)Enum中一些方法(不包含繼承Object的)的解釋:

protected Object clone()
拋出CloneNotSupportedException異常
int compareTo(E o)
將此枚舉與指定的對(duì)象比較以進(jìn)行排序
boolean equals(Object other)
如果指定的對(duì)象和此枚舉常量相等就返回true
protected void finalize()
枚舉類不能有finalize方法
Class<E> getDeclaringClass()
返回和此枚舉常量的枚舉類型相對(duì)應(yīng)的Class對(duì)象
int hashCode()
返回此枚舉常量的哈希值
String name()
返回此枚舉常量的名稱,與其枚舉聲明中聲明的一致
int ordinal()
返回此枚舉常量的序號(hào)(它在枚舉聲明中的位置,初始常量的序號(hào)默認(rèn)為0)
String toString()
返回聲明中包含的枚舉常量的名稱
static <T extends Enum<T>> valueOf(Class<T> enumType,String name)
返回指定名稱和類型的枚舉常量

這里有一點(diǎn)需要注意的是,我們不能在定義的枚舉類型的構(gòu)造器里調(diào)用父類Enum的構(gòu)造器,這是不被允許的,在編譯器編譯時(shí)會(huì)自動(dòng)幫我們調(diào)用去初始化枚舉值的聲明。雖然Enum是一個(gè)抽象類,但是自定義枚舉類型仍然會(huì)在編譯階段使用Enum的構(gòu)造函數(shù),不過在創(chuàng)建子類對(duì)象時(shí)到底有沒有創(chuàng)建父類對(duì)象還存在分歧,具體可以看看知乎上的這篇文章。

2. 枚舉類的使用

2.1 定義

public enum Day {
    SUNDAY, MONDAY, TUESDAY, WEDNESDAY,
    THURSDAY, FRIDAY, SATURDAY 
}

枚舉值之間使用","分隔,如果后面沒有屬性或方法,最后加不加";"都行。但如果有就一定要加";"

public enum PurOrderSplitEnum {
    NORELEASEERPBILLCOUNT("1", "待發(fā)布"),
    RELEASEERPBILLCOUNT("2", "待買方確認(rèn)"),
    PURCONFIRMINGCOUNT("3", "買方變更中");
    
    private String code;
    private String name;
    public static Map<String, String> code2name = new HashMap();
    public static Map<String, String> code2Muname = new HashMap();

    private PurOrderSplitEnum(String code, String name) {
        this.code = code;
        this.name = name;
    }

    public String getCode() {
        return this.code;
    }

    public String getName() {
        return this.name;
    }

    static {
        code2name.put(NORELEASEERPBILLCOUNT.getCode(), NORELEASEERPBILLCOUNT.getName());
        code2name.put(RELEASEERPBILLCOUNT.getCode(), RELEASEERPBILLCOUNT.getName());
        code2name.put(PURCONFIRMINGCOUNT.getCode(), PURCONFIRMINGCOUNT.getName());
   
        code2Muname.put(NORELEASEERPBILLCOUNT.getCode(), "NORELEASEERPBILLCOUNT");
        code2Muname.put(RELEASEERPBILLCOUNT.getCode(), "RELEASEERPBILLCOUNT");
        code2Muname.put(PURCONFIRMINGCOUNT.getCode(), "PURCONFIRMINGCOUNT");
     
    }
}

2.2 結(jié)合Switch

在switch中使用枚舉,可以讓我們的代碼可讀性更好

public class EnumTest {
    Day day;
    
    public EnumTest(Day day) {
        this.day = day;
    }
    
    public void tellItLikeItIs() {
        switch (day) {
            case MONDAY:
                System.out.println("周一還行吧");
                break;
                    
            case FRIDAY:
                System.out.println("周五很nice");
                break;
                         
            case SATURDAY: 
            case SUNDAY:
                System.out.println("周末超級(jí)棒");
                break;
                        
            default:
                System.out.println("額....");
                break;
        }
    }
    
    public static void main(String[] args) {
        EnumTest firstDay = new EnumTest(Day.MONDAY);
        firstDay.tellItLikeItIs();
        EnumTest thirdDay = new EnumTest(Day.WEDNESDAY);
        thirdDay.tellItLikeItIs();
        EnumTest fifthDay = new EnumTest(Day.FRIDAY);
        fifthDay.tellItLikeItIs();
        EnumTest sixthDay = new EnumTest(Day.SATURDAY);
        sixthDay.tellItLikeItIs();
        EnumTest seventhDay = new EnumTest(Day.SUNDAY);
        seventhDay.tellItLikeItIs();
    }
    //輸出結(jié)果
    /*
    周一還行吧
    額....
    周五很nice
    周末超級(jí)棒
    周末超級(jí)棒
    */

2.3 valueOf方法

把字符串轉(zhuǎn)成對(duì)應(yīng)類型的枚舉值,如:

 String str = "SPRING";
 Season season2 = Season.valueOf(str);
 //Season season2 = Season.valueOf(Season.class,str);跟上面是等價(jià)的
 System.out.println(season2 == season1);//true

2.4 values()和其他方法

values()方法可以返回所有定義的枚舉值,name()方法返回的是枚舉的名稱,不是枚舉值里面的屬性名稱,這里需要注意一下。

 Season season1 = Season.SPRING;
 for(Season season:Season.values()){
     System.out.println(season);
 }
 season1.show();
 System.out.println(season1.name());
 System.out.println("SPIRNG的序號(hào):" + season1.ordinal());
//輸出結(jié)果
/*
Season{seasonName='spring', seasonDesc='春暖花開'}
Season{seasonName='summer', seasonDesc='夏日炎炎'}
Season{seasonName='autumn', seasonDesc='秋高氣爽'}
Season{seasonName='winter', seasonDesc='白雪茫茫'}
春天在哪里
SPRING
0
*/

2.5 使用接口組織枚舉

在接口中定義枚舉類,可以將數(shù)據(jù)分組。

public interface Animal {  
    enum Dog implements Animal {  
         HUSKY,GOLDEN_RETRIEVER,ALASKAN_MALAMUTE
    }  
       
    enum Cat implements Animal {  
         PERSIAN,RAGDOLL,BIRMAN  
    }  
}
// Animal dog = Animal.Dog.ALASKAN_MALAMUTE;
// Animal cat = Animal.Cat.BIRMAN;

2.6 EnumSet和EnumMap

Set類型是枚舉集或Map的key是枚舉值時(shí),使用這兩者將會(huì)更加高效。這里只摘取Java API文檔的部分說明,詳細(xì)的方法大家可以自己查找文檔。

EnumMap

用于枚舉類型鍵的專用Map實(shí)現(xiàn)。 枚舉映射中的所有鍵必須來自創(chuàng)建映射時(shí)顯式或隱式指定的單個(gè)枚舉類型。 枚舉映射在內(nèi)部表示為數(shù)組。 這種表現(xiàn)非常緊湊和高效。

枚舉映射按其鍵的自然順序(枚舉常量的聲明順序)維護(hù)。 這反映在集合視圖(keySet(),entrySet()和values())返回的迭代器中。

集合視圖返回的迭代器非常一致:它們永遠(yuǎn)不會(huì)拋出ConcurrentModificationException,它們可能會(huì)也可能不會(huì)顯示迭代進(jìn)行過程中對(duì)映射所做的任何修改的影響。

Key不允許為null。 嘗試插入null將拋出NullPointerException。 但是,嘗試測(cè)試是否存在空鍵或刪除空鍵將正常運(yùn)行。 value允許為null。

與大多數(shù)集合實(shí)現(xiàn)一樣,EnumMap不同步。 如果多個(gè)線程同時(shí)訪問枚舉映射,并且至少有一個(gè)線程修改了映射,則應(yīng)該在外部進(jìn)行同步。 這通常通過在自然封裝枚舉映射的某個(gè)對(duì)象上同步來完成。 如果不存在此類對(duì)象,則應(yīng)使用Collections.synchronizedMap(java.util.Map <K,V>)方法“包裝”映射。 這最好在創(chuàng)建時(shí)完成,以防止意外的不同步訪問:

? Map <EnumKey,V> m= Collections.synchronizedMap(new EnumMap <EnumKey,V>(...));

說明:所有基本操作都在恒定時(shí)間內(nèi)執(zhí)行。 它們很可能(雖然不能保證)比它們對(duì)應(yīng)的HashMap更快。

EnumSet

用于枚舉類型的專用Set實(shí)現(xiàn)。 枚舉集中的所有元素必須來自單個(gè)枚舉類型,該類型在創(chuàng)建集時(shí)顯式或隱式指定。 枚舉集在內(nèi)部表示為位向量。 這種表現(xiàn)非常緊湊和高效。 這個(gè)類的空間和時(shí)間性能應(yīng)該足夠好,以允許它作為傳統(tǒng)的基于int的“位標(biāo)志”的高性能、類型安全的替代品。 如果它們的參數(shù)也是枚舉集,即使批量操作(例如containsAll和retainAll)也應(yīng)該非??焖俚剡\(yùn)行。

迭代器方法返回的迭代器以其自然順序(枚舉常量聲明的順序)遍歷元素。 返回的迭代器是弱一致的:它永遠(yuǎn)不會(huì)拋出ConcurrentModificationException,它可能會(huì)也可能不會(huì)顯示迭代進(jìn)行過程中對(duì)集合所做的任何修改的影響。

不允許使用空元素。 嘗試插入null元素將拋出NullPointerException。 但是,嘗試測(cè)試null元素的存在或刪除一個(gè)元素將正常工作。

與大多數(shù)集合實(shí)現(xiàn)一樣,EnumSet不同步。 如果多個(gè)線程同時(shí)訪問枚舉集,并且至少有一個(gè)線程修改了該集,則應(yīng)該在外部進(jìn)行同步。 這通常通過在自然封裝枚舉集的某個(gè)對(duì)象上進(jìn)行同步來實(shí)現(xiàn)。 如果不存在此類對(duì)象,則應(yīng)使用Collections.synchronizedSet(java.util.Set <T>)方法“包裝”該集合。 這最好在創(chuàng)建時(shí)完成,以防止意外的不同步訪問:

Set <MyEnum> s = Collections.synchronizedSet(EnumSet.noneOf(MyEnum.class));

說明:所有基本操作都在恒定時(shí)間內(nèi)執(zhí)行。 它們很可能(雖然不能保證)比它們對(duì)應(yīng)的的HashSet快得多。 如果它們的參數(shù)也是枚舉集,即使批量操作也會(huì)在恒定時(shí)間內(nèi)執(zhí)行。

2.7 使用枚舉實(shí)現(xiàn)單例模式

基于枚舉類型的特性,Effective Java建議使用枚舉來實(shí)現(xiàn)單例模式。

public enum Singleton{
    INSTANCE;
    public void doSomething{
        //...
    }
}
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

友情鏈接更多精彩內(nèi)容