黑馬程序員-Java基礎加強之枚舉

--------- 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();
    }
}
/*-------------------------------
打印結果:
    秋天
    解落三秋葉,能開二月花。過江千尺浪,入竹萬竿斜。
    冬天
    牆角數枝梅,凌寒獨自開。遙知不是雪,爲有暗香來。
-------------------------------*/


四、其它

  • 當枚舉只有一個成員時,就可以作爲一種單例的實現方式。




--------- android培訓java培訓、期待與您交流!----------

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章