探秘 Java 中的枚举(enum)

本文包括:

  1. 枚举由来
  2. 如何使用?
  3. 枚举类特性
  4. 单例设计模式
  5. 定义特殊结构枚举
  6. 星期输出中文的案例
  7. 枚举类API
Paste_Image.png

枚举(enum)

1、枚举由来

  • 定义一个仅容许特定数据类型值的有限集合。

  • 例如,你可能需要一个称为“等级”,且仅可被赋值为“A”,“B”,“C”,“F”等值的类型,任何其他值在此类型中都是非法的,所以在Java5中产生了枚举。

2、如何使用?

  1. 如下使用关键字enum定义了一个简单的Grade对象,使用起来和其他的Java类型一样。

     public enum Grade {A, B, C, F}; //习惯全部大写!
    
  2. 假设有一个员工类,在员工类定义角色属性,角色只有三个值: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";// 非法数据,但不会报错。如果不小心字符串拼错了,程序出问题,所以直接使用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; // 非法数据,但不会报错。
            }
        }            
      
    • 第三次尝试:

        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; // 非法数据,但不会报错。
            }
        }    
      
        class Role2 {
            public static final int BOSS = 1;
            public static final int MANAGER = 2;
            public static final int WORKER = 3;
        }
      
    • 第四次尝试:

        private Role3 role3; // 在 JDK5 之前 没有枚举,通过自定义类 实现枚举功能
      
        class Employee { // 员工类
      
            private Role3 role3; // 在 JDK5 之前 没有枚举,通过自定义类 实现枚举功能
      
            public static void main(String[] args) {
                Employee employee = new Employee();
                //通过自定义Role3实现了枚举功能
                employee.role3 = Role3.BOSS; // 可读性好
                employee.role3 = 1; // 非法数据,但会报错!
                employee.role3 = new Role3(); // 非法数据,但不会报错!检查不出来
            
            }
        }    
      
        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 之后 引入枚举,使用枚举表示多个角色
      
            public static void main(String[] args) {
                Employee employee = new Employee();
                // 使用枚举之后
                employee.role4 = Role4.MANAGER; // 可读性良好,阻止非法数据
                employee.role4 = new Role4(); // 枚举具有私有构造方法,所以会报错!因此第五次尝试优于第四次尝试
            
            }
        }    
      
        enum Role4 { // JDK5 以后引用枚举技术 简化对象创建 ---- 功能等价于 Role3
            BOSS, MANAGER, WORKER;
        }
      

3、枚举类特性

  • 枚举类也是一种特殊形式的Java类。

  • 枚举类中声明的每一个枚举值代表枚举类的一个实例对象。(可以查看.class文件,在里面明显有每一个枚举值的实例对象)

  • 与Java类的普通类一样,在声明枚举类的同时,也可以声明属性、方法和构造函数,但枚举类的构造函数必须是私有的。(IDE中写构造器时会自动生成private,删除private也不会报错,默认就是private)

  • 枚举类也可以实现接口,或继承抽象类。

  • Java5中扩展了switch语句,它除了可以接受int、byte、chart、short外,还可以接受一
    个枚举类型。(Java7 switch 可以接受六种 : 多了一种String)

  • 若枚举类只有一个值,则可以当作单例设计模式使用。(见4、单例设计模式)

4、单例设计模式

  1. 单例设计模式写法,必须包括以下三点,因此枚举类若只有一个值,则可以当作单例设计模式使用。

    1. 私有构造器

    2. private static 成员对象

    3. public static 获得成员对象方法

  2. 懒汉式 和 饿汉式

    • 饿汉:在创建对象时 直接进行初始化

    • 懒汉:在获取对象时 进行初始化

  3. 示例:

     // 饿汉
     class B {
         // 1、私有构造器
         private B() {
         }
     
         // 2、private static 对象成员
         private static B b = new B();
     
         // 3、提供public static 获得成员方法 , 获得唯一实例
         public static B getInstance() {
             return b;
         }
     }
     
     // 懒汉
     class C {
         // 1、私有构造器
         private C() {
         }
     
         // 2、private static 对象成员
         private static C c;
     
         // 3、提供public static 获得成员方法 , 获得唯一实例
         public static C getInstance() {
             if (c == null) {
                 c = new C(); // 懒汉式
             }
             return c;
         }
     }
     
     enum A {
         TEST; // 该枚举中只有 TEST实例 ,相当于单例设计模式!
     }
    

5、定义特殊结构枚举

  • 在枚举实例定义过程中

    1. 向枚举构造器传入参数。

    2. 在枚举中定义方法。

    3. 通过匿名内部类实现枚举中抽象方法。

  • . 示例:
    public enum EnumConstructorTest {
    A(10) { // 通过匿名内部类实现抽象方法
    @Override
    public void show() {
    }

          },
          B(20) {
              @Override
              public void show() {
      
              }
      
          }; // 创建枚举值时,传入构造方法参数
      
          // 构造方法 带有参数
          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() {
              // 根据枚举对象 名字 返回响应中文星期
              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() 返回枚举对象名称

    • ordinal() 返回枚举对象下标

    • valueOf(Class enumClass, String name) 将String类型 枚举对象名称 转换为对应的Class类型枚举对象

  2. 自定义的枚举类,在编译阶段自动生成下面方法(即在API中没有,在编译后的.class文件可以找到):

    • valueOf(String name) 自定义枚举类的方法,转换枚举对象

    • values() 获得所有枚举对象实例数组

  3. 五种方法示例:

     public enum Color {
         BLUE, RED, YELLOW;
         // public static final Color color = new Color();
     
         Color() {// 构造器默认private
         }
     }
     
     public class EnumAPITest {
         @Test
         public void demo1() {
             // 任何 enum 定义 枚举类 都是默认 继承 Enum 类 ,使用Enum 中方法
             Color color = Color.RED; // 枚举对象 不能 new 获得,使用已经创建好对象
     
             // name 方法返回 枚举 实例名称
             System.out.println(color.name());
     
             // ordinal 方法 返回 枚举对象 下标
             System.out.println(color.ordinal());
     
             // valueOf 方法 将 String 类型 枚举对象 名称 ----- 转换为相应枚举对象
             String name = "YELLOW";
             Color yellow = Enum.valueOf(Color.class, name); // 将 name 转换 成响应枚举对象
             System.out.println(yellow.ordinal());
     
             // 使用枚举类 编译后生成两个方法
    
             // values 获得 所有 枚举对象数组
             Color[] colors = Color.values();
             System.out.println(Arrays.toString(colors));
     
             // 生成valueOf 只接受String 类型枚举名称,将名称转换为当前枚举类对象
             String name2 = "BLUE";
             Color blue = Color.valueOf(name2); // 将name2 枚举对象名称 转换 Color对象枚举实例
             System.out.println(blue.ordinal());
         }
     }
    
  4. 枚举对象、枚举对象下标、枚举对象名,三者之间的相互转换(用上五种方法,必须掌握!):

     @Test
     // 枚举对象、枚举对象下标、枚举对象名称表示之间的转换
     public void demo2() {
         // 第一种 已知枚举对象 --- 获得下标和名称
         Color blue = Color.BLUE;
         // 获得下标
         System.out.println(blue.ordinal());
         // 获得名称
         System.out.println(blue.name());
    
         System.out.println("----------------------------------");
         // 第二种 已知枚举对象 下标 --- 获得枚举对象实例 和 名称
         int index = 1;
         // 获得枚举对象
         Color red = Color.values()[index];
         // 获得名称
         System.out.println(red.name());
    
         System.out.println("---------------------------------");
         // 第三种 已知枚举对象名称 ----- 获得枚举对象实录 和 下标
         String name = "YELLOW";
    
         // 获得实例
         Color c1 = Enum.valueOf(Color.class, name);
         Color c2 = Color.valueOf(name);
    
         // 获得下标
         System.out.println(c1.ordinal());
         System.out.println(c2.ordinal());
     }
    

参考阅读:
《Effective Java 中文版 第2版》chapter 6
《JAVA 5.0 TIGER程序高手秘籍》chapter 3

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,911评论 5 460
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 82,014评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 142,129评论 0 320
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,283评论 1 264
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,159评论 4 357
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,161评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,565评论 3 382
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,251评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,531评论 1 292
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,619评论 2 310
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,383评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,255评论 3 313
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,624评论 3 299
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,916评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,199评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,553评论 2 342
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,756评论 2 335

推荐阅读更多精彩内容

  • 1. Java基础部分 基础部分的顺序:基本语法,类相关的语法,内部类的语法,继承相关的语法,异常的语法,线程的语...
    子非鱼_t_阅读 31,505评论 18 399
  • Java经典问题算法大全 /*【程序1】 题目:古典问题:有一对兔子,从出生后第3个月起每个月都生一对兔子,小兔子...
    赵宇_阿特奇阅读 1,835评论 0 2
  • 小编费力收集:给你想要的面试集合 1.C++或Java中的异常处理机制的简单原理和应用。 当JAVA程序违反了JA...
    八爷君阅读 4,544评论 1 114
  • 参考:angular2+highcharts+chart.js 如何在Ionic2项目中使用第三方JavaScr...
    忻芷月光阅读 2,221评论 1 7
  • 咔嚓、咔、咔、咔嚓嘣!你在干嘛呢,快来一起嗑呀!不然你就要输了!咦,这是在干什么呢?哦,原来这是在玩一一嗑瓜子比赛...
    江南1118阅读 211评论 0 0