枚舉類
爲什麼會有枚舉類?
假如說程序中有一些顏色的狀態,或者說消息的類型等,在JDK1.5之前,只能用常量來進行表示
public class TestEnum {
public static final int RED = 1;
public static final int BLACK = 2;
public static final int GREEN = 3;
public static void main(String[] args) {
int num = 1;
System.out.println(RED == num);
}
}
//true
num不是一個顏色,就與實際不太相符合,於是JDK1.5中引入了枚舉類型
enum EnumColor {
RED, BLACK, GREEN;
public static void main(String[] args) {
EnumColor color = EnumColor.BLACK;
switch (color) {
case RED:
System.out.println("紅色");
break;
case BLACK:
System.out.println("黑色");
break;
case GREEN:
System.out.println("綠色");
break;
}
}
}
//黑色
枚舉類的使用
- 類的對象只有有限個,確定的(星期,性別,季節…)
- 當需要定義一組常量
- 使用enum定義的枚舉類默認繼承了java.lang. Enum類,因此不能再繼承其他類
- 枚舉類的構造器只能使用private權限修飾符
- 枚舉類的所有實例必須在枚舉類中顯式列出(,分隔結尾)。列出的實例
系統會自動添加public static final修飾必須在枚舉類的第一行聲明枚舉類對象 - 枚舉不能再類外直接實例化,也不能被繼承
如果枚舉有參數可以添加對應的構造函數,但要注意:枚舉的構造函數默認是私有的。所以不能被繼承
枚舉就是定義了一些狀態或者常見集合,一般不需要單獨實例化,用的時候到枚舉類中找合適的即可
枚舉的構造函數不能使用public和protected修飾。
枚舉類的常用方法
方法名稱 | 描述 |
---|---|
values() | 以數組形式返回枚舉類型的所有成員 |
ordinal() | 獲取枚舉成員的索引位置 |
valueOf() | 將普通字符串轉換爲枚舉實例 |
compareTo() | 比較兩個枚舉成員在定義時的順序 |
enum EnumColor {
RED, BLACK, GREEN;
public static void main(String[] args) {
// 以數組的方式返回所有的枚舉成員
EnumColor[] colors = EnumColor.values();
// 打印每個枚舉成員的索引
for(int i = 0; i < colors.length; ++i){
System.out.println(colors[i] + ":" + colors[i].ordinal());
}
// 將字符串GREEN轉化爲枚舉類型
EnumColor color1 = EnumColor.valueOf("GREEN");
System.out.println(color1);
// 在進行轉換時,如果有對應的枚舉類型則轉換,否則拋出IllegalArgumentException
// EnumColor color2 = EnumColor.valueOf("YELLOW");//定義的枚舉類沒有YELLOW
// System.out.println(color2);
EnumColor color2 = EnumColor.valueOf("BLACK");//定義的枚舉類沒有YELLOW
System.out.println(color2);
System.out.println("-------------------------------------");
System.out.println("枚舉實例的比較");
// 注意此處的比較是使用枚舉成員的索引來比較了
EnumColor black = EnumColor.BLACK;
EnumColor red = EnumColor.RED;
System.out.println(black.compareTo(red));
System.out.println(BLACK.compareTo(RED));
System.out.println(RED.compareTo(BLACK));
}
//結果:
RED:0
BLACK:1
GREEN:2
GREEN
BLACK
-------------------------------------
枚舉實例的比較
1
1
-1
枚舉的構造
上述枚舉類型有一個不太友好的地方是,枚舉中只有枚舉常量,拿到一個枚舉常量後還是不知道其是什麼顏色的,因此可以給枚舉增加構造函數。
enum EnumColor {
RED("紅色", 1), BLACK("黑色", 2), GREEN("綠色", 3);
private String color;
private int key;
// 注意:枚舉的構造函數默認是私有的,此處不能增加非private的訪問權限進行限制
/*public*/EnumColor(String color, int key){
this.color = color;
this.key = key;
}
public static EnumColor getEnum(String str){
for (EnumColor color : EnumColor.values()){
if(color.color.equals(str))
return color;
}
return null;
}
public static void main(String[] args) {
System.out.println(EnumColor.getEnum("紅色"));
}
}
枚舉類型能被反射嗎?
不能不能不能!
通過看反射的源碼,可以看出反射在通過newInstance創建對象時,會檢查該類是否ENUM修飾,如果是則拋出異常,反射失敗。
爲什麼枚舉實現單例模式是安全的?
enum Singleton5 {//枚舉類的單例模式
INSTANCE;
public Singleton5 getInstance(){
return INSTANCE;
}
}
1、 私有化構造器並不保險,通過反射機制調用私有構造器。而枚舉類不能被反射,所以可以防止反射攻擊
//模擬反射攻擊
class Singleton {//雙重校驗鎖,性能佳線程安全
private static Singleton4 instance=null;
private Singleton4() {}
public static Singleton4 getInstance() {
if(instance==null) {
synchronized (Singleton4.class) {
if (instance==null) {
//new對象分爲三個操作
//分配內存
//調用構造方法初始化
//賦值
instance=new Singleton4();
}
}
}
return instance;
}
}
public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
Singleton s=Singleton.getInstance();
Singleton sUsual=Singleton.getInstance();
Constructor<Singleton> constructor=Singleton.class.getDeclaredConstructor();
constructor.setAccessible(true);
Singleton sReflection=constructor.newInstance();
System.out.println(s+"\n"+sUsual+"\n"+sReflection);
System.out.println("正常情況下,實例化兩個實例是否相同:"+(s==sUsual));
System.out.println("通過反射攻擊單例模式情況下,實例化兩個實例是否相同:"+(s==sReflection));
}
//com.lxp.pattern.singleton.Singleton@1540e19d
//com.lxp.pattern.singleton.Singleton@1540e19d
//.lxp.pattern.singleton.Singleton@677327b6
//正常情況下,實例化兩個實例是否相同:true
//通過反射攻擊單例模式情況下,實例化兩個實例是否相同:false
2.避免序列化問題(任何一個readObject方法,不管是顯式的還是默認的,它都會返回一個新建的實例,這個新建的實例不同於該類初始化時創建的實例)
枚舉類的優缺點
1、優點
枚舉常量更簡單安全 。
枚舉具有內置方法 ,代碼更優雅
2. 缺點
不可繼承,無法擴展