探秘 Java 中的枚舉(enum)

本文包括:

  1. 枚舉由來(lái)
  2. 如何使用?
  3. 枚舉類特性
  4. 單例設(shè)計(jì)模式
  5. 定義特殊結(jié)構(gòu)枚舉
  6. 星期輸出中文的案例
  7. 枚舉類API
Paste_Image.png

枚舉(enum)

1、枚舉由來(lái)

  • 定義一個(gè)僅容許特定數(shù)據(jù)類型值的有限集合。

  • 例如,你可能需要一個(gè)稱為“等級(jí)”,且僅可被賦值為“A”,“B”,“C”,“F”等值的類型,任何其他值在此類型中都是非法的,所以在Java5中產(chǎn)生了枚舉。

2、如何使用?

  1. 如下使用關(guān)鍵字enum定義了一個(gè)簡(jiǎn)單的Grade對(duì)象,使用起來(lái)和其他的Java類型一樣。

     public enum Grade {A, B, C, F}; //習(xí)慣全部大寫!
    
  2. 假設(shè)有一個(gè)員工類,在員工類定義角色屬性,角色只有三個(gè)值:BOSS、MANAGER、WORKDER。

    • 第一次嘗試:

        class Employee { // 員工類
      
            private String role1;
      
            public static void main(String[] args) {
                Employee employee = new Employee();
                employee.role1 = "BOSS";
                employee.role1 = "MANAGER";
                employee.role1 = "MANGAER";// 非法數(shù)據(jù),但不會(huì)報(bào)錯(cuò)。如果不小心字符串拼錯(cuò)了,程序出問(wèn)題,所以直接使用String類型作為角色屬性,行不通!
            }
        }
      
    • 第二次嘗試:

        class Employee { // 員工類
      
            private int role2; // 1 BOSS 2 MANAGE 3 WORKER
      
            public static void main(String[] args) {
                Employee employee = new Employee();
                    employee.role2 = 1; // 可讀性太差,如果角色有上十種,上百種呢?
                    employee.role2 = 4; // 非法數(shù)據(jù),但不會(huì)報(bào)錯(cuò)。
            }
        }            
      
    • 第三次嘗試:

        class Employee { // 員工類
      
            private int role2; // 1 BOSS 2 MANAGE 3 WORKER
      
            public static void main(String[] args) {
                Employee employee = new Employee();
                employee.role2 = Role2.BOSS; // 相比于第二次嘗試,可讀性好了很多
                employee.role2 = -1; // 非法數(shù)據(jù),但不會(huì)報(bào)錯(cuò)。
            }
        }    
      
        class Role2 {
            public static final int BOSS = 1;
            public static final int MANAGER = 2;
            public static final int WORKER = 3;
        }
      
    • 第四次嘗試:

        private Role3 role3; // 在 JDK5 之前 沒(méi)有枚舉,通過(guò)自定義類 實(shí)現(xiàn)枚舉功能
      
        class Employee { // 員工類
      
            private Role3 role3; // 在 JDK5 之前 沒(méi)有枚舉,通過(guò)自定義類 實(shí)現(xiàn)枚舉功能
      
            public static void main(String[] args) {
                Employee employee = new Employee();
                //通過(guò)自定義Role3實(shí)現(xiàn)了枚舉功能
                employee.role3 = Role3.BOSS; // 可讀性好
                employee.role3 = 1; // 非法數(shù)據(jù),但會(huì)報(bào)錯(cuò)!
                employee.role3 = new Role3(); // 非法數(shù)據(jù),但不會(huì)報(bào)錯(cuò)!檢查不出來(lái)
            
            }
        }    
      
        class Role3 { // 枚舉功能 類
            public static final Role3 BOSS = new Role3();
            public static final Role3 MANAGER = new Role3();
            public static final Role3 WORKER = new Role3();
        
            private Role3() {
            }
        }
      
    • 第五次嘗試:

        class Employee { // 員工類
      
            private Role4 role4; // 在 JDK5 之后 引入枚舉,使用枚舉表示多個(gè)角色
      
            public static void main(String[] args) {
                Employee employee = new Employee();
                // 使用枚舉之后
                employee.role4 = Role4.MANAGER; // 可讀性良好,阻止非法數(shù)據(jù)
                employee.role4 = new Role4(); // 枚舉具有私有構(gòu)造方法,所以會(huì)報(bào)錯(cuò)!因此第五次嘗試優(yōu)于第四次嘗試
            
            }
        }    
      
        enum Role4 { // JDK5 以后引用枚舉技術(shù) 簡(jiǎn)化對(duì)象創(chuàng)建 ---- 功能等價(jià)于 Role3
            BOSS, MANAGER, WORKER;
        }
      

3、枚舉類特性

  • 枚舉類也是一種特殊形式的Java類。

  • 枚舉類中聲明的每一個(gè)枚舉值代表枚舉類的一個(gè)實(shí)例對(duì)象。(可以查看.class文件,在里面明顯有每一個(gè)枚舉值的實(shí)例對(duì)象)

  • 與Java類的普通類一樣,在聲明枚舉類的同時(shí),也可以聲明屬性、方法和構(gòu)造函數(shù),但枚舉類的構(gòu)造函數(shù)必須是私有的。(IDE中寫構(gòu)造器時(shí)會(huì)自動(dòng)生成private,刪除private也不會(huì)報(bào)錯(cuò),默認(rèn)就是private)

  • 枚舉類也可以實(shí)現(xiàn)接口,或繼承抽象類。

  • Java5中擴(kuò)展了switch語(yǔ)句,它除了可以接受int、byte、chart、short外,還可以接受一
    個(gè)枚舉類型。(Java7 switch 可以接受六種 : 多了一種String)

  • 若枚舉類只有一個(gè)值,則可以當(dāng)作單例設(shè)計(jì)模式使用。(見(jiàn)4、單例設(shè)計(jì)模式)

4、單例設(shè)計(jì)模式

  1. 單例設(shè)計(jì)模式寫法,必須包括以下三點(diǎn),因此枚舉類若只有一個(gè)值,則可以當(dāng)作單例設(shè)計(jì)模式使用。

    1. 私有構(gòu)造器

    2. private static 成員對(duì)象

    3. public static 獲得成員對(duì)象方法

  2. 懶漢式 和 餓漢式

    • 餓漢:在創(chuàng)建對(duì)象時(shí) 直接進(jìn)行初始化

    • 懶漢:在獲取對(duì)象時(shí) 進(jìn)行初始化

  3. 示例:

     // 餓漢
     class B {
         // 1、私有構(gòu)造器
         private B() {
         }
     
         // 2、private static 對(duì)象成員
         private static B b = new B();
     
         // 3、提供public static 獲得成員方法 , 獲得唯一實(shí)例
         public static B getInstance() {
             return b;
         }
     }
     
     // 懶漢
     class C {
         // 1、私有構(gòu)造器
         private C() {
         }
     
         // 2、private static 對(duì)象成員
         private static C c;
     
         // 3、提供public static 獲得成員方法 , 獲得唯一實(shí)例
         public static C getInstance() {
             if (c == null) {
                 c = new C(); // 懶漢式
             }
             return c;
         }
     }
     
     enum A {
         TEST; // 該枚舉中只有 TEST實(shí)例 ,相當(dāng)于單例設(shè)計(jì)模式!
     }
    

5、定義特殊結(jié)構(gòu)枚舉

  • 在枚舉實(shí)例定義過(guò)程中

    1. 向枚舉構(gòu)造器傳入?yún)?shù)。

    2. 在枚舉中定義方法。

    3. 通過(guò)匿名內(nèi)部類實(shí)現(xiàn)枚舉中抽象方法。

  • . 示例:
    public enum EnumConstructorTest {
    A(10) { // 通過(guò)匿名內(nèi)部類實(shí)現(xiàn)抽象方法
    @Override
    public void show() {
    }

          },
          B(20) {
              @Override
              public void show() {
      
              }
      
          }; // 創(chuàng)建枚舉值時(shí),傳入構(gòu)造方法參數(shù)
      
          // 構(gòu)造方法 帶有參數(shù)
          private EnumConstructorTest(int a) {
          }
      
          // 在枚舉中定義方法
          @Override
          public String toString() {
              return super.toString();
          }
      
          public void print() {
              System.out.println("TEST");
          }
      
          // 在枚舉中定義抽象方法
          public abstract void show();
      }
    

6、星期輸出中文的案例

  • 加深印象:為什么要用枚舉類,如何使用枚舉類。

      public class WeekDayTest {
          public static void main(String[] args) {
              WeekDay1 day1 = WeekDay1.Fri;
              day1.show();
      
              WeekDay2 day2 = WeekDay2.Wed;
              day2.show();
          }
      }
      
      enum WeekDay2 {
          Mon {
              @Override
              public void show() {
                  System.out.println("星期一");
              }
          },
          Tue {
              @Override
              public void show() {
                  System.out.println("星期二");
              }
          },
          Wed {
              @Override
              public void show() {
                  System.out.println("星期三");
              }
          },
          Thu {
              @Override
              public void show() {
                  System.out.println("星期四");
              }
          },
          Fri {
              @Override
              public void show() {
                  System.out.println("星期五");
              }
          },
          Sat {
              @Override
              public void show() {
                  System.out.println("星期六");
              }
          },
          Sun {
              @Override
              public void show() {
                  System.out.println("星期日");
              }
          };
          public abstract void show();
      }
      
      enum WeekDay1 {
          Mon, Tue, Wed, Thu, Fri, Sat, Sun;
      
          // 編寫方法 show
          public void show() {
              // 根據(jù)枚舉對(duì)象 名字 返回響應(yīng)中文星期
              if (this.name().equals("Mon")) {
                  System.out.println("星期一");
              } else if (this.name().equals("Tue")) {
                  System.out.println("星期二");
              } else if (this.name().equals("Wed")) {
                  System.out.println("星期三");
              } else if (this.name().equals("Thu")) {
                  System.out.println("星期四");
              } else if (this.name().equals("Fri")) {
                  System.out.println("星期五");
              } else if (this.name().equals("Sat")) {
                  System.out.println("星期六");
              } else if (this.name().equals("Sun")) {
                  System.out.println("星期日");
              }
          }
      }
    

7、枚舉類API

  1. Java中聲明的枚舉類,均是java.lang.Enum類的子類,它繼承了Enum類的所有方法。
    常用方法:

    • name() 返回枚舉對(duì)象名稱

    • ordinal() 返回枚舉對(duì)象下標(biāo)

    • valueOf(Class enumClass, String name) 將String類型 枚舉對(duì)象名稱 轉(zhuǎn)換為對(duì)應(yīng)的Class類型枚舉對(duì)象

  2. 自定義的枚舉類,在編譯階段自動(dòng)生成下面方法(即在API中沒(méi)有,在編譯后的.class文件可以找到):

    • valueOf(String name) 自定義枚舉類的方法,轉(zhuǎn)換枚舉對(duì)象

    • values() 獲得所有枚舉對(duì)象實(shí)例數(shù)組

  3. 五種方法示例:

     public enum Color {
         BLUE, RED, YELLOW;
         // public static final Color color = new Color();
     
         Color() {// 構(gòu)造器默認(rèn)private
         }
     }
     
     public class EnumAPITest {
         @Test
         public void demo1() {
             // 任何 enum 定義 枚舉類 都是默認(rèn) 繼承 Enum 類 ,使用Enum 中方法
             Color color = Color.RED; // 枚舉對(duì)象 不能 new 獲得,使用已經(jīng)創(chuàng)建好對(duì)象
     
             // name 方法返回 枚舉 實(shí)例名稱
             System.out.println(color.name());
     
             // ordinal 方法 返回 枚舉對(duì)象 下標(biāo)
             System.out.println(color.ordinal());
     
             // valueOf 方法 將 String 類型 枚舉對(duì)象 名稱 ----- 轉(zhuǎn)換為相應(yīng)枚舉對(duì)象
             String name = "YELLOW";
             Color yellow = Enum.valueOf(Color.class, name); // 將 name 轉(zhuǎn)換 成響應(yīng)枚舉對(duì)象
             System.out.println(yellow.ordinal());
     
             // 使用枚舉類 編譯后生成兩個(gè)方法
    
             // values 獲得 所有 枚舉對(duì)象數(shù)組
             Color[] colors = Color.values();
             System.out.println(Arrays.toString(colors));
     
             // 生成valueOf 只接受String 類型枚舉名稱,將名稱轉(zhuǎn)換為當(dāng)前枚舉類對(duì)象
             String name2 = "BLUE";
             Color blue = Color.valueOf(name2); // 將name2 枚舉對(duì)象名稱 轉(zhuǎn)換 Color對(duì)象枚舉實(shí)例
             System.out.println(blue.ordinal());
         }
     }
    
  4. 枚舉對(duì)象、枚舉對(duì)象下標(biāo)、枚舉對(duì)象名,三者之間的相互轉(zhuǎn)換(用上五種方法,必須掌握!):

     @Test
     // 枚舉對(duì)象、枚舉對(duì)象下標(biāo)、枚舉對(duì)象名稱表示之間的轉(zhuǎn)換
     public void demo2() {
         // 第一種 已知枚舉對(duì)象 --- 獲得下標(biāo)和名稱
         Color blue = Color.BLUE;
         // 獲得下標(biāo)
         System.out.println(blue.ordinal());
         // 獲得名稱
         System.out.println(blue.name());
    
         System.out.println("----------------------------------");
         // 第二種 已知枚舉對(duì)象 下標(biāo) --- 獲得枚舉對(duì)象實(shí)例 和 名稱
         int index = 1;
         // 獲得枚舉對(duì)象
         Color red = Color.values()[index];
         // 獲得名稱
         System.out.println(red.name());
    
         System.out.println("---------------------------------");
         // 第三種 已知枚舉對(duì)象名稱 ----- 獲得枚舉對(duì)象實(shí)錄 和 下標(biāo)
         String name = "YELLOW";
    
         // 獲得實(shí)例
         Color c1 = Enum.valueOf(Color.class, name);
         Color c2 = Color.valueOf(name);
    
         // 獲得下標(biāo)
         System.out.println(c1.ordinal());
         System.out.println(c2.ordinal());
     }
    

參考閱讀:
《Effective Java 中文版 第2版》chapter 6
《JAVA 5.0 TIGER程序高手秘籍》chapter 3

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,333評(píng)論 6 531
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 98,491評(píng)論 3 416
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人,你說(shuō)我怎么就攤上這事。” “怎么了?”我有些...
    開(kāi)封第一講書人閱讀 176,263評(píng)論 0 374
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我,道長(zhǎng),這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書人閱讀 62,946評(píng)論 1 309
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 71,708評(píng)論 6 410
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書人閱讀 55,186評(píng)論 1 324
  • 那天,我揣著相機(jī)與錄音,去河邊找鬼。 笑死,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,255評(píng)論 3 441
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書人閱讀 42,409評(píng)論 0 288
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 48,939評(píng)論 1 335
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 40,774評(píng)論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 42,976評(píng)論 1 369
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,518評(píng)論 5 359
  • 正文 年R本政府宣布,位于F島的核電站,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 44,209評(píng)論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書人閱讀 34,641評(píng)論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書人閱讀 35,872評(píng)論 1 286
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 51,650評(píng)論 3 391
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 47,958評(píng)論 2 373

推薦閱讀更多精彩內(nèi)容

  • 1. Java基礎(chǔ)部分 基礎(chǔ)部分的順序:基本語(yǔ)法,類相關(guān)的語(yǔ)法,內(nèi)部類的語(yǔ)法,繼承相關(guān)的語(yǔ)法,異常的語(yǔ)法,線程的語(yǔ)...
    子非魚(yú)_t_閱讀 31,717評(píng)論 18 399
  • Java經(jīng)典問(wèn)題算法大全 /*【程序1】 題目:古典問(wèn)題:有一對(duì)兔子,從出生后第3個(gè)月起每個(gè)月都生一對(duì)兔子,小兔子...
    趙宇_阿特奇閱讀 1,885評(píng)論 0 2
  • 小編費(fèi)力收集:給你想要的面試集合 1.C++或Java中的異常處理機(jī)制的簡(jiǎn)單原理和應(yīng)用。 當(dāng)JAVA程序違反了JA...
    八爺君閱讀 4,635評(píng)論 1 114
  • 參考:angular2+highcharts+chart.js 如何在Ionic2項(xiàng)目中使用第三方JavaScr...
    忻芷月光閱讀 2,252評(píng)論 1 7
  • 咔嚓、咔、咔、咔嚓嘣!你在干嘛呢,快來(lái)一起嗑呀!不然你就要輸了!咦,這是在干什么呢?哦,原來(lái)這是在玩一一嗑瓜子比賽...
    江南1118閱讀 217評(píng)論 0 0