Java之路:列舉
列舉(Enum)
一、定義
enum 列舉值 { 列舉值表};
// 例如
enum WeekDay { Mon, Tue, Wed, Thu, Fri, Sat,Sun };
其中,enum是Java中的關鍵字。在列舉值表中應羅列出所有的可用值,這些值也稱為列舉元素。
列舉變數也可用不同的方式說明,如先定義後說明、定義的同時說明或直接說明。
設有變數a、b、c被定義為上述的列舉型別WeekDay,可採用下述任意一種方式。
enum WeekDay{Mon,Tue,Wed,Thu,Fri,Sat,Sun}; //先定義
enum WeekDay a,b,c; //後說明
// 或者為:
enum WeekDay {Mon,Tue,Wed,Thu,Fri,Sat,Sun}a,b,c; //定義的同時說明
// 或者為:
enum {Mon,Tue,Wed,Thu,Fri,Sat,Sun}a,b,c //直接說明,即定義無名列舉
1、在Java中使用簡單的列舉
enum Color { 紅色, 綠色, 藍色 };
public class EnumColor {
public static void main(String[] args) {
Color c1 = Color.紅色;
Color c2;
c2 = Color.綠色; // 通過“ 列舉名.列舉值 ”獲得列舉值
System.out.println(c1);
System.out.println(c2);
}
}
【結果】
2、在switch語句中使用列舉
enum Color { 紅色, 綠色, 藍色 };
public class SwitchEnum {
public static void main(String[] args) {
Color c1 = Color.紅色;
switch(c1) {
case 紅色: System.out.println("我是紅色!"); break;
case 綠色: System.out.println ("我是綠色!"); break;
case 藍色: System.out.println("我是藍色!"); break;
}
}
}
【結果】
注:由於Java採用的是Unicode的字串編碼方式,所以列舉值也可支援中文。
二、列舉類和列舉關鍵字
列舉型別的出現,有助於簡潔程式的程式碼量,減少出錯量。在大多數情況下,列舉類和列舉關鍵字是相互依存的。
列舉關鍵字是定義列舉型別時必不可少的宣告,而列舉類則是規定的列舉型別母類。
1、列舉類
列舉類(Enum類)是在Java.lang包下定義的一個公共類,它的作用是用來構造新的列舉型別。
這是在JDK1.5之後Java推出的一個新的類,用來彌補關於列舉這一常用集合在Java中的不足。同時, Enum類中的構造方法可方便對程式碼的操作。
在Enum類中定義了大約十多個方法,每一種方法都是對用Enum建立的列舉物件實施操作,所以**Enum類是一個完整的型別。**它擁有自己的方法,當建立一個關於Enum的型別物件時,便可呼叫其中的方法來完善對於列舉型別的操作。
下表列出了Enum類中的主要方法:
enum Color { 紅色, 綠色, 藍色 };
public class ValueOfEnum {
public static void main(String[] args) {
/* 在上面的Enum類“方法概要”的表格中,並沒有列出來values()方法。
* 實際上,這是編譯器新增的隱式方法,
* 如果一個物件被宣告為enum型別,
* 編譯器會自動給該型別新增一個隱含的方法values(),
* 它的原型為:public static E[] values();
* 通過values()方法獲得列舉型別中各個物件的值
*/
Color[] allColor =Color.values();
for( Color mc : allColor )
System.out.println(mc);
}
}
【結果】
【注】
列舉關鍵字(enum)是定義的一個列舉型別。 實際上,在此次定義的過程中,通過enum關鍵字相當於定義了一個類,並且此類將繼承自Enum類。
2、列舉關鍵字
在Enum類中的構造方法中是保護型別的,實際上對於每一個列舉的物件一旦宣告之後,就表示自動呼叫此構造方法,所有的編號方式均採用自動編號的方式進行。
在沒有對編號做出特殊宣告時,Java虛擬機器一般將對被建立的列舉型別物件自動編號,編號從0開始。如下:
enum Color { 紅色, 綠色, 藍色 };
public class OrderOfEnum {
public static void main(String[] args) {
Color[] allColor = Color.values();
for( MyColor mc : allColor )
print(mc.name() + " --> " + mc.ordinal());
}
}
【結果】
3、列舉類與列舉關鍵字之間的關係
實際上,使用enum關鍵字,相當於定義了一個類,該類將繼承自Enum類。
而在Enum類中的方法,訪問許可權都是保護(protected)型別的,因此這些方法都可以在enum關鍵字定義的物件中直接使用。
在下面的範例中,先用enum關鍵字定義了一個列舉物件Color,然後呼叫Enum類中的valueof()方法:
enum Color { 紅色, 綠色, 藍色 };
public class ValueOfEnum {
public static void main(String[] args) {
Color c = Color.valueOf(Color.class, "紅色");
System.out.println(c);
}
}
【結果】
【代詳解】
Color列舉型別呼叫在Enum類中方法valueof(),這是一個靜態方法,用於返回指定列舉類中指定名稱的列舉值。
在本例中,方法valueof()的第一個引數“Color.class”,用來返回Color的型別——列舉型別,第二個引數是字串常量“紅色”。
4、使用列舉類的注意事項
(1) 如果enum定義的列舉類訪問許可權定義為public,則需要單獨形成一個.java檔案,即不可與包含main方法的public類同處於同一個檔案。如果enum定義的列舉類訪問許可權為預設型別,即enum關鍵字前沒有修飾符,則enum定義的物件可在同一個包裡訪問,無需重複定義。
若要在其他包中訪列舉類Color,可用如下方法:
package color; // 在color包中定義Color
public enum Color { 紅色, 綠色, 藍色 }
// 在另一個包中訪問Color
package enum_color;
import color.Color; // 用import關鍵字匯入Color即可
public class EnumColor2 {
public static void main(String[] args) {
System.out.println(Color.紅色);
}
}
(2) 使用enum定義的列舉類,預設繼承於java.lang.Enum類。使用enum定義的列舉類,預設會使用final修飾,因此該類無法派生子類。
(3) 使用enum定義的列舉類,其所有的列舉值(實際上是該類的例項)必須在列舉類的第一行顯示列出,否則這個列舉類將永遠不能產生例項。在列出這些例項(即列舉值)時,**系統會自動新增public static final修飾。**也即是說,所有列舉值都是public static final型別的。
(4) 所有使用enum定義的列舉類,都會由系統隱式提供一個values()方法,該方法可方便地遍歷所有的列舉值。
三、深入瞭解列舉
1、列舉的構造方法
列舉的使用非常靈活,它可應用於程式碼中的各個角落,只要定義的物件具有列舉的形式,均可使用列舉對其進行定義。這樣在減少程式碼量的同時,也可增加程式碼的可讀性和可操作性。
enum NewColor{
/*
* 定義三個列舉值RED GREEN BLUE,
* 實際上,這三個列舉值 是NewColor列舉類的三個例項化物件
* 注意:列舉類定義的物件必須出現 在該類有效程式碼的第一行,
* 否則,編無法通過
* 一旦列舉類的構造方法定義之後,
* 那麼所有的列舉物件都必須顯示呼叫此構造方法,例如:RED("紅色",4)
* NewColor定義的三個物件
*/
RED("紅色",4), // 呼叫構造方法NewColor(String name, int index)
// 注意 後面是逗號
GREEN("綠色",5), // 注意 後面是逗號
BLUE("藍色",6); // 注意 後面是分號
// 成員變數
private String name;
private int index;
// NewColor類私有構造方法, 外部無法呼叫列舉構造方法
private NewColor(String name,int index) {
this.name=name;
this.index=index;
}
// 普通方法,通過索引獲得其對應的名稱
public static String getName(int index) {
for( NewColor c : NewColor.values() ) {
if(c.getIndex()==index) { return c.name; }
}
return null;
}
public String getName() { return name; }
// 普通方法,通過索引設定其對應的名稱
public static void setName(int index,String name) {
for( NewColor c : NewColor.values() ) {
if(c.getIndex() == index) {
c.name=name;
return;
}
}
}
public int getIndex() { return index; }
// 普通方法,通過名字設定其對應的標號
public static void setIndex(int index, String name) {
for( NewColor c : NewColor.values() ) {
if(c.getName() == name) {
c.index=index;
return;
}
}
}
}
public class EnumConstructor {
public static void main(String[] args) {
System.out.println("------------輸出列舉中的元素----------");
System.out.println(NewColor.RED.getIndex()+"---->"+NewColor.RED.getName());
System.out.println(NewColor.GREEN.getIndex()+"---->"+NewColor.GREEN.getName());
System.out.println(NewColor.BLUE.getIndex()+"---->"+NewColor.BLUE.getName());
System.out.println("------在自定義編號和屬性值之後,測試-------");
NewColor.setName(4,"黑色"); //重新設定名稱,將4號設為黑色
System.out.println("4---->"+NewColor.getName(4));
NewColor.setIndex(7,"黑色"); //重新設定索引編號,將黑色設為7號
System.out.println("7---->"+NewColor.getName(7));
}
}
【結果】
注:列舉類定義的物件必須出現 在該類有效程式碼的第一行,否則,編無法通過。一旦列舉類的構造方法定義之後,那麼所有的列舉物件都必須顯示呼叫此構造方法。 例如:RED(“紅色”,4)。
2、列舉的介面
在前文中,我們已經提到,所有的列舉事實上都繼承自java.lang.Enum類。
這表明列舉已有了一個“父類”,那它不能再繼承其他類,那麼是不是列舉就“註定孤獨地”使用繼承自Enum類中的方法呢?
Java語言的設計者是聰明的,他們為使用者“關閉了一扇門”,但又為“開啟了另外一扇窗”,那這扇窗就是Java中的一個重要概念——介面(Interface)。
在Java的一個類中,既包括資料成員(即屬性),又包括對這些屬性的操作(即方法)。
而在Java的一個介面中,在某種程度上,它可視為一個簡化版本的“類”,其僅包括一系列方法和一些不可更改的靜態常量。
Java中的介面不能直接拿來例項化任何物件,因為在其內僅僅提供了方法的宣告,即只有方法的特徵而沒有方法的具體實現,它不具備例項化物件的條件。換句話說,介面“生來”就是要被繼承的,在繼承中被具體化實現。
介面中的這些方法可在不同的地方被不同的類實現,從而可表現出具有不同的“個性化”行為(功能)。
Java語言中的介面,只是對要實現該介面方法的所有類提出了一個共享的固定格式的協議(protocol)。這些協議固定了在其內的靜態常量和方法簽名(方法名+引數列表)形式,而繼承這個介面的類,就可在其類中對這些繼承而來的方法,“獨立自主、自由發揮”地實現這些方法。
Java語言雖然不支援一個類有多個直接的父類(即不支援多繼承),但一個類卻可以實現(implements)多個介面。
介面彌補了Java類不能多繼承缺點,單繼承和多介面的雙重設計既保持了類的資料安全,也間接實現了多繼承。Java的“另外一扇窗””就在這裡開啟。
列舉介面的實現:
package enum_interface;
// 定義一個介面,注意 public 型別必須在它自己的包中定義
public interface ColorInterface {
public String getColor();
}
package newcolor;
// 實現列舉介面,注意 public 型別必須在它自己的包中定義
public enum NewColor implements ColorInterface {
紅色
{
public String getColor() {
return "RED";
}
}, // 注意,每個列舉值之間是用逗號“,”分隔開的,最後一個才用“;”
綠色
{
public String getColor() {
return "GREEN";
}
},
藍色
{
public String getColor() {
return "BLUE";
}
}; // 最後一個列舉值用分號“;”結尾
}
package testcolor;
// 測試列舉介面,注意 public 型別必須在它自己的包中定義
public class TestColor {
public static void main(String[] args) {
for(NewColor nc : NewColor.values())
System.out.println(nc.ordinal() + " --> " +
nc.name() + " : " +
nc.getColor());
}
}
【結果】
3、在列舉中的定義抽象方法
在介面中定義的方法,由於只有方法原型,沒有具體實現,這些方法可視為是“抽象的”方法,不管用不用修飾符abstract都是一樣。
實際上,Java也可以在列舉中直接定義一個或多個抽象方法。 需要注意的是,這種情況下,需要為列舉中的每個物件單獨地實現此方法。
enum NewColor3 {
紅色 // 每個列舉物件都需要實現所定義的抽象方法
{
public String getColor() {
return "RED";
}
},
綠色
{
public String getColor() {
return "GREEN";
}
},
藍色
{
public String getColor() {
return "BLUE";
}
};
/* 定義抽象方法,
* 注意,之所以把抽象方法的宣告放在這裡,
* 是因為,列舉類定義的物件必須出現 在該類有效程式碼的第一行,
* 否則,編無法通過。
* 也即,如果把宣告放在“紅色”前面,會揚子報錯
*/
public abstract String getColor();
}
public class AbstractEnum {
public static void main(String[] args) {
for(NewColor3 nc : NewColor3.values()) {
print(nc.ordinal() + " --> " + ": " + nc.getColor());
}
}
}
【結果】
注:在Java程式設計實現上,所謂抽象方法,就是用關鍵字abstract修飾且沒有實現主體的方法。
四、使用列舉類的注意事項
1、列舉型別不能用 public 和 protected 修飾符修飾構造方法。構造方法的許可權只能是private或者friendly,friendly是當沒有修飾符時的預設許可權。
因為列舉的這種特性,所以列舉物件是無法在程式中通過直接呼叫其構造方法來初始化的。
public enum Color {
//Color定義的三個物件
RED("紅色",4),
GREEN("綠色",5),
BLUE("藍色",6);
//成員變數
private String name;
private int index;
//構造方法
/*
* 構造方法Color只能是 private 或 friendly
* 如果是 public 或 protected 會報錯
*/
private Color(String name,int index) {
this.name=name;
this.index=index;
}
}
2、定義列舉型別時,如果是簡單型別,那麼最後一個列舉值後可以不加分號。 如:
enum Color { 紅色, 綠色, 藍色 };
但是如果列舉中包含有方法,那麼最後一個列舉值後面程式碼必須要用分號“;”隔開。 如:
package newcolor;
public enum NewColor implements ColorInterface {
紅色
{
public String getColor() {
return "RED";
}
}, // 注意,每個列舉值之間是用逗號“,”分隔開的,最後一個才用“;”
綠色
{
public String getColor() {
return "GREEN";
}
},
藍色
{
public String getColor() {
return "BLUE";
}
}; // 最後一個列舉值用分號“;”結尾
}
3、列舉類不可以被繼承
在列舉類內部可以定義一個或多個抽象方法時,那這個列舉類為什麼不能用abstract修飾呢?
如果一個列舉類用abstract修飾,那麼就說明需要其他類繼承這個所謂的“抽象列舉類”,而在Java中,規定列舉是不能被繼承的。
因此,列舉不能用abstract修飾,只能在每個列舉的例項實現每個抽象方法。