--------- android培訓、java培訓、期待與您交流!----------
一、爲什麼要有枚舉?
問題:要定義季節、星期向或性別的變量,該怎麼定義?假設用1-4分別表示春、夏、秋、冬,但有人可能會寫成int SEASON_SPRING = 0;或即使使用常量方式也不無法阻止意外,因爲int型的數據是可以參與運算的。
枚舉就是要讓某個類型的變量的取值只能爲若干個固定值中的一個,否則,編譯器就會報錯。
枚舉可以讓編譯器在編譯時就可以控制源程序中填寫的非法值,普通變量的方式在開發階段無法實現這一目標。
二、那麼該如何用普通類實現枚舉功能呢?
以定義一個Seanson類來模擬枚舉功能。
①私有化構造方法,加private
②每個元素分別用一個公有的靜態成員變量表示,public static ,再加上final表示不能派生子類。
③可以有若干公有方法或抽象方法。
④採用抽象方法定義nextSeason就是將大量的if.else語句轉移成了一個個獨立的類。
如下代碼:
package com.itheima.blog; public class Season { private final String name; //分別創建Season的四個子類,並用public static final修飾 public static final Season SPRING = new Season("春天"); public static final Season SUMMER = new Season("夏天"); public static final Season FALL = new Season("秋天"); public static final Season WINTER = new Season("冬天"); //獲取當前季節的下一個季節 public Season nextSeason(){ if (this == SPRING) { return SUMMER; } if (this == SUMMER) { return FALL; } if (this == FALL) { return WINTER; } if (this == WINTER) { return SPRING; } return null; /* 經測試,switch不能使用此方法,this會報錯; * switch(this){ case SPRING: return SUMMER; case SUMMER: return FALL; case FALL: return WINTER; case WINTER: return SPRING; default: return null;*/ } //將構造方法私有化 private Season(String name){ this.name = name; } //因爲枚舉值是固定的,所以只提供獲取方法。 public String getName(){ return this.name; } }
也可以將以上代碼改成抽象方法的方式:
如:
package com.itheima.blog; //因爲聲明瞭抽象方法,所以該類也必須顯示聲明爲抽象類。 public abstract class Season { //定義一個field該field也是不能被直接訪問的,並且不可變。 private final String name; //創建一個Season對象的匿名實例,並內部提供方法。該方法稱爲抽象方法 public static final Season SPRING = new Season("春天"){ public Season nextSeason(){ return SUMMER; } }; public static final Season SUMMER = new Season("夏天"){ public Season nextSeason(){ return FALL; } }; public static final Season FALL = new Season("秋天"){ public Season nextSeason(){ return WINTER; } }; public static final Season WINTER = new Season("冬天"){ public Season nextSeason(){ return SPRING; } }; //聲明該類的抽象方法 public abstract Season nextSeason(); //將構造方法私有化 private Season(String name){ this.name = name; } //因爲枚舉值是固定的,所以只提供獲取方法。 public String getName(){ return this.name; } }
測試類:
package com.itheima.blog; public class SeasonTest { public static void main(String[] args) { Season s = Season.WINTER; System.out.println(s.nextSeason().getName());//結果都是春天 } }
三、枚舉類的基本應用
和class關鍵字聲明的類一樣,一個Java源文件中最多隻能定義一個public訪問權限的枚舉類,且該Java源文件也必須和該枚舉類的類名相同。
它具有如下幾個特點:
①枚舉類可以實現一個或多個接口,使用enum定義的枚舉類默認繼承了java.lang.Enum類,而不是繼承java.lang.Object類,其中java.lang.Enum類實現了java.lang.Serializable和java.lang.Comparable兩個接口。
②使用enum定義、非抽象的枚舉類默認會使用final修飾,因此枚舉類不能派生子類。
//經測試:發現不能顯式用final修飾。
③枚舉類的構造器只能使用private訪問修飾符,如果省略了構造器的訪問控制符,則默認使用
private修飾;如果強制指定訪問控制符,則只能指定private修飾符。
④枚舉類的所有實例必須在枚舉類的第一行顯式列出,否則這個枚舉類永遠都不能產生實例。列出這些實例時,系統會自動添加public static final修飾,無須程序員顯式添加。
⑤所有枚舉類都提供了一個values方法,該方法用來遍歷該枚舉類的所有的枚舉實例。
下面以例子來說明,枚舉類的使用:
1、例子一(最簡單的枚舉類):
package com.itheiam.blog; public enum Season { //必須定義在第一行,元素之間用,號隔開,後面可以加;也可不加 SPRING,SUMMER,FALL,WINTER } class SeasonTest { public static void main(String[] args) { //直接獲取Season的實例,並返回Season對象,說明有static修飾。 Season s = Season.FALL; //下面演示一些方法 System.out.println(s.name());//打印FALL System.out.println(s.toString());//打印FALL //所以toString方法和name()方法效果是一樣的。 System.out.println(s.ordinal());//打印2,說明返回的是所謂的角標。 //靜態方法 //將字符串轉成對應的枚舉實例,很重要。 System.out.println(Season.valueOf("SUMMER"));//SUMMER System.out.println(Season.valueOf(Season.class, "FALL"));//FALL //遍歷該枚舉類的所有實例。 for (Season season : Season.values()) { System.out.println(season); } } }
2、例子二(枚舉類的Field、方法和構造器):
package com.itheiam.blog; public enum Season2 { //如果枚舉值後有參數,則必須加最後必須加";"否則會報錯 SPRING("春天"),SUMMER("夏天"),FALL("秋天"),WINTER("冬天"); //這樣定義也是可以的。 //春天("SPRING"),夏天("SUMMER"),秋天("FALL"),冬天("WINTER"); //應該將枚舉類的Field用private final修飾 /*因爲我們將所有的Field都用final修飾符來修飾,所以必須在 構造器裏爲這些Field指定初始值(或者在定義Field時指定默認值, 或者在初始化塊中指定初始值,但這兩種情況不常見)因此,我們 需要爲枚舉類顯式定義帶參數的構造器。*/ private final String name; //顯式聲明私有化的構造方法 private Season2(String name) { this.name = name; } //獲取枚舉值,也就是枚舉實例對應的名稱 public String getName() { return this.name; } } class SeasonTest2 { public static void main(String[] args) { //通過枚舉的實例對象來獲取枚舉值 String str = Season2.FALL.getName(); System.out.println(str);//秋天 System.out.println(Season2.SPRING);//SPRING //通過Enum的靜態方法valueOf方法來獲取指定枚舉類的枚舉值。 Season2 s = Enum.valueOf(Season2.class, "FALL"); System.out.println(s.getName());//秋天 } }
3、例子三(實現接口的枚舉類):
package com.itheiam.blog; //定義一個接口,用來獲取Season信息 interface SeasonInfo { void info(); } //讓枚舉類實現接口 public enum Season3 implements SeasonInfo { //如果枚舉值後有參數,則必須加最後必須加";"否則會報錯 SPRING("春天"),SUMMER("夏天"),FALL("秋天"),WINTER("冬天"); //應該將枚舉類的Field用private final修飾 private final String name; //顯式聲明私有化的構造方法 private Season3(String name) { this.name = name; } //獲取枚舉值,也就是枚舉實例對應的名稱 public String getName() { return this.name; } //因爲實現了SeasonInfo接口,所以必須實現其方法 public void info() { System.out.println("這是一個獲取季節信息的方法!"); } } class SeasonTest3 { public static void main(String[] args) { //通過Enum的靜態方法valueOf方法來獲取指定枚舉類的枚舉值。 Season3 s1 = Enum.valueOf(Season3.class, "FALL"); System.out.println(s1.getName()); s1.info(); Season3 s2 = Enum.valueOf(Season3.class, "WINTER"); System.out.println(s2.getName()); s2.info(); } } /*------------------------------- 打印結果: 秋天 這是一個獲取季節信息的方法! 冬天 這是一個獲取季節信息的方法! -------------------------------*/
由以上例子可以看出,如果由枚舉來實現接口裏的方法,則每個枚舉值在調用方法時都有相同的行爲方式(因爲方法體完全一樣)。如果需要每個枚舉值在調用該方法時呈現出不同的行爲方式,則可以讓每個枚舉實例分別來實現該方法,從而讓不同的枚舉值調用該方法時具有不同的行爲方式。程序如下:
package com.itheiam.blog; interface SeasonInfo2 { void info(); } //讓枚舉類實現接口 public enum Season4 implements SeasonInfo2 { //如果枚舉值後有參數,則必須加最後必須加";"否則會報錯 SPRING("春天") { public void info() { System.out.println("紅豆生南國,春來發幾枝。願君多采擷,此物最相思。"); } }, SUMMER("夏天") { public void info() { System.out.println("垂緌飲清露,流響出疏桐。居高聲自遠, 非是藉秋風。"); } }, FALL("秋天") { public void info() { System.out.println("解落三秋葉,能開二月花。過江千尺浪,入竹萬竿斜。"); } }, WINTER("冬天") { public void info() { System.out.println("牆角數枝梅,凌寒獨自開。遙知不是雪,爲有暗香來。"); } }; //應該將枚舉類的Field用private final修飾 private final String name; //顯式聲明私有化的構造方法 private Season4(String name) { this.name = name; } //獲取枚舉值,也就是枚舉實例對應的名稱 public String getName() { return this.name; } } class SeasonTest4 { public static void main(String[] args) { //通過Enum的靜態方法valueOf方法來獲取指定枚舉類的枚舉值。 Season4 s1 = Enum.valueOf(Season4.class, "FALL"); System.out.println(s1.getName()); s1.info(); Season4 s2 = Enum.valueOf(Season4.class, "WINTER"); System.out.println(s2.getName()); s2.info(); } } /*-------------------------------- 打印結果: 秋天 解落三秋葉,能開二月花。過江千尺浪,入竹萬竿斜。 冬天 牆角數枝梅,凌寒獨自開。遙知不是雪,爲有暗香來。 --------------------------------*/
4、例子四(包含抽象方法的枚舉類):
package com.itheiam.blog; /*雖然枚舉內部定義了抽象方法,但這裏不能使用abstract關鍵字將枚舉類 定義成抽象類(因爲系統自動會爲它添加abstract關鍵字),否則會報錯。*/ //public abstract enum Season5 public enum Season5 { SPRING("春天") { public void describeInfo() { System.out.println("紅豆生南國,春來發幾枝。願君多采擷,此物最相思。"); } }, SUMMER("夏天") { public void describeInfo() { System.out.println("垂緌飲清露,流響出疏桐。居高聲自遠, 非是藉秋風。"); } }, FALL("秋天") { public void describeInfo() { System.out.println("解落三秋葉,能開二月花。過江千尺浪,入竹萬竿斜。"); } }, WINTER("冬天") { public void describeInfo() { System.out.println("牆角數枝梅,凌寒獨自開。遙知不是雪,爲有暗香來。"); } }; //爲了好跟前面的方法比較,繼續定義一個Field private final String name; /*因爲枚舉類需要顯式創建枚舉值,而不是作爲父類,所以定義每個枚舉值時必須爲抽象 方法實現,否則將出現編譯錯誤。*/ public abstract void describeInfo(); private Season5(String name) { this.name = name; } public String getName() { return this.name; } } class SeasonTest5 { public static void main(String[] args) { //通過Enum的靜態方法valueOf方法來獲取指定枚舉類的枚舉值。 Season5 s1 = Enum.valueOf(Season5.class, "FALL"); System.out.println(s1.getName()); s1.describeInfo(); Season5 s2 = Enum.valueOf(Season5.class, "WINTER"); System.out.println(s2.getName()); s2.describeInfo(); } } /*------------------------------- 打印結果: 秋天 解落三秋葉,能開二月花。過江千尺浪,入竹萬竿斜。 冬天 牆角數枝梅,凌寒獨自開。遙知不是雪,爲有暗香來。 -------------------------------*/
四、其它
當枚舉只有一個成員時,就可以作爲一種單例的實現方式。