最近在外面實(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{
//...
}
}