Java 语言中 Enum 类型的使用介绍_enum java语言-程序员宅基地

技术标签: java  

Enum 类型的介绍

枚举类型(Enumerated Type) 很早就出现在编程语言中,它被用来将一组类似的值包含到一种类型当中。而这种枚举类型的名称则会被定义成独一无二的类型描述符,在这一点上和常量的定义相似。不过相比较常量类型,枚举类型可以为申明的变量提供更大的取值范围。

举个例子来说明一下,如果希望为彩虹描绘出七种颜色,你可以在 Java 程序中通过常量定义方式来实现。

清单 1. 常量定义
 Public static class RainbowColor { 
    
    // 红橙黄绿青蓝紫七种颜色的常量定义
    public static final int RED = 0; 
    public static final int ORANGE = 1; 
    public static final int YELLOW = 2; 
    public static final int GREEN = 3; 
    public static final int CYAN = 4; 
    public static final int BLUE = 5; 
    public static final int PURPLE = 6; 
 }

使用的时候,你可以在程序中直接引用这些常量。但是,这种方式还是存在着一些问题。

  1. 类型不安全

由于颜色常量的对应值是整数形,所以程序执行过程中很有可能给颜色变量传入一个任意的整数值,导致出现错误。

  1. 没有命名空间

由于颜色常量只是类的属性,当你使用的时候不得不通过类来访问。

  1. 一致性差

因为整形枚举属于编译期常量,所以编译过程完成后,所有客户端和服务器端引用的地方,会直接将整数值写入。这样,当你修改旧的枚举整数值后或者增加新的枚举值后,所有引用地方代码都需要重新编译,否则运行时刻就会出现错误。

  1. 类型无指意性

由于颜色枚举值仅仅是一些无任何含义的整数值,如果在运行期调试时候,你就会发现日志中有很多魔术数字,但除了程序员本身,其他人很难明白其奥秘。

如何定义 Enum 类型

为了改进 Java 语言在这方面的不足弥补缺陷,5.0 版本 SDK 发布时候,在语言层面上增加了枚举类型。枚举类型的定义也非常的简单,用 enum 关键字加上名称和大括号包含起来的枚举值体即可,例如上面提到的彩虹颜色就可以用新的 enum 方式来重新定义:

 enum RainbowColor { RED, ORANGE, YELLOW, GREEN, CYAN, BLUE, PURPLE }

从上面的定义形式来看,似乎 Java 中的枚举类型很简单,但实际上 Java 语言规范赋予枚举类型的功能非常的强大,它不仅是简单地将整形数值转换成对象,而是将枚举类型定义转变成一个完整功能的类定义。这种类型定义的扩展允许开发者给枚举类型增加任何方法和属性,也可以实现任意的接口。另外,Java 平台也为 Enum 类型提供了高质量的实现,比如默认实现 Comparable 和 Serializable 接口,让开发者一般情况下不用关心这些细节。

回到本文的主题上来,引入枚举类型到底能够给我们开发带来什么样好处呢?一个最直接的益处就是扩大 switch 语句使用范围。5.0 之前,Java 中 switch 的值只能够是简单类型,比如 int、long、char, 有了枚举类型之后,就可以使用对象了。这样一来,程序的控制选择就变得更加的方便,看下面的例子:

清单 2. 定义 Enum 类型
 // 定义一周七天的枚举类型			
 public enum WeekDayEnum { Mon, Tue, Wed, Thu, Fri, Sat, Sun } 

 // 读取当天的信息
 WeekDayEnum today = readToday(); 
 
 // 根据日期来选择进行活动
 switch(today) { 
  Mon: do something; break; 
  Tue: do something; break; 
  Wed: do something; break; 
  Thu: do something; break; 
  Fri: do something; break; 
  Sat: play sports game; break; 
  Sun: have a rest; break; 
 }


对于这些枚举的日期,JVM 都会在运行期构造成出一个简单的对象实例一一对应。这些对象都有唯一的 identity,类似整形数值一样,switch 语句就根据此来进行执行跳转。

如何定制 Enum 类型

除了以上这种最常见的枚举定义形式外,如果需要给枚举类型增加一些复杂功能,也可以通过类似 class 的定义来给枚举进行定制。比如要给 enum 类型增加属性,可以像下面这样定义:

清单 3. 定制枚举类型
 // 定义 RSS(Really Simple Syndication) 种子的枚举类型
 public enum NewsRSSFeedEnum { 
    // 雅虎头条新闻 RSS 种子
    YAHOO_TOP_STORIES("http://rss.news.yahoo.com/rss/topstories"), 
    
    //CBS 头条新闻 RSS 种子
    CBS_TOP_STORIES("http://feeds.cbsnews.com/CBSNewsMain?format=xml"), 
    
    // 洛杉矶时报头条新闻 RSS 种子
    LATIMES_TOP_STORIES("http://feeds.latimes.com/latimes/news?format=xml"); 
        
    // 枚举对象的 RSS 地址的属性
    private String rss_url; 
        
    // 枚举对象构造函数
    private NewsRSSFeedEnum(String rss) { 
        this.rss_url = rss; 
    } 
        
    // 枚举对象获取 RSS 地址的方法
    public String getRssURL() { 
        return this.rss_url; 
    } 
 }

上面头条新闻的枚举类型增加了一个 RSS 地址的属性 , 记录头条新闻的访问地址。同时,需要外部传入 RSS 访问地址的值,因而需要定义一个构造函数来初始化此属性。另外,还需要向外提供方法来读取 RSS 地址。

如何避免错误使用 Enum

不过在使用 Enum 时候有几个地方需要注意:

  1. enum 类型不支持 public 和 protected 修饰符的构造方法,因此构造函数一定要是 private 或 friendly 的。也正因为如此,所以枚举对象是无法在程序中通过直接调用其构造方法来初始化的。
  2. 定义 enum 类型时候,如果是简单类型,那么最后一个枚举值后不用跟任何一个符号;但如果有定制方法,那么最后一个枚举值与后面代码要用分号';'隔开,不能用逗号或空格。
  3. 由于 enum 类型的值实际上是通过运行期构造出对象来表示的,所以在 cluster 环境下,每个虚拟机都会构造出一个同义的枚举对象。因而在做比较操作时候就需要注意,如果直接通过使用等号 ( ‘ == ’ ) 操作符,这些看似一样的枚举值一定不相等,因为这不是同一个对象实例。

看下面的这个例子:

清单 4. 避免错误使用 Enum 示例
 // 定义一个一周七天的枚举类型
 package example.enumeration.codes; 

 public enum WeekDayEnum { 
    Mon(1), Tue(2), Wed(3), Thu(4), Fri(5), Sat(6), Sun(7); 

    private int index; 

    WeekDayEnum(int idx) { 
        this.index = idx; 
    } 

    public int getIndex() { 
        return index; 
    } 
 } 

 // 客户端程序,将一个枚举值通过网络传递给服务器端
 package example.enumeration.codes; 

 import java.io.IOException; 
 import java.io.ObjectOutputStream; 
 import java.io.OutputStream; 
 import java.net.InetSocketAddress; 
 import java.net.Socket; 
 import java.net.UnknownHostException; 

 public class EnumerationClient { 

    public static void main(String... args) throws UnknownHostException, IOException { 
        Socket socket = new Socket(); 
  // 建立到服务器端的连接
        socket.connect(new InetSocketAddress("127.0.0.1", 8999)); 
    // 从连接中得到输出流
        OutputStream os = socket.getOutputStream(); 
        ObjectOutputStream oos = new ObjectOutputStream(os); 
  // 将星期五这个枚举值传递给服务器端
        oos.writeObject(WeekDayEnum.Fri); 
        oos.close(); 
        os.close(); 
        socket.close(); 
    } 
 } 

 // 服务器端程序,将从客户端收到的枚举值应用到逻辑处理中
 package example.enumeration.codes; 

 import java.io.*; 
 import java.net.ServerSocket; 
 import java.net.Socket; 

 public class EnumerationServer { 

    public static void main(String... args) throws IOException, ClassNotFoundException { 
        ServerSocket server = new ServerSocket(8999); 
  // 建立服务器端的网络连接侦听
        Socket socket = server.accept(); 
  // 从连接中获取输入流
        InputStream is = socket.getInputStream(); 
        ObjectInputStream ois = new ObjectInputStream(is); 
  // 读出客户端传递来的枚举值
        WeekDayEnum day = (WeekDayEnum) ois.readObject(); 
  // 用值比较方式来对比枚举对象
        if (day == WeekDayEnum.Fri) { 
            System.out.println("client Friday enum value is same as server's"); 
        } else if (day.equals(WeekDayEnum.Fri)) { 
            System.out.println("client Friday enum value is equal to server's"); 
        } else { 
            System.out.println("client Friday enum value is not same as server's"); 
        } 
        
  // 用 switch 方式来比较枚举对象
        switch (day) { 
            case Mon: 
                System.out.println("Do Monday work"); 
                break; 
            case Tue: 
                System.out.println("Do Tuesday work"); 
                break; 
            case Wed: 
                System.out.println("Do Wednesday work"); 
                break; 
            case Thu: 
                System.out.println("Do Thursday work"); 
                break; 
            case Fri: 
                System.out.println("Do Friday work"); 
                break; 
            case Sat: 
                System.out.println("Do Saturday work"); 
                break; 
            case Sun: 
                System.out.println("Do Sunday work"); 
                break; 
            default: 
                System.out.println("I don't know which is day"); 
                break; 
        } 
        
        ois.close(); 
        is.close(); 
        socket.close(); 
    } 
 }

打印结果如下:

 client Friday enum value is same as server's 
 Do Friday work

通过程序执行结果,我们能够发现在分布式条件下客户端和服务端的虚拟机上都生成了一个枚举对象,即使看起来一样的 Fri 枚举值,如果使用等号‘ == ’进行比较的话会出现不等的情况。而 switch 语句则是通过 equal 方法来比较枚举对象的值,因此当你的枚举对象较复杂时候,你就需要小心 override 与比较相关的方法,防止出现值比较方面的错误。

Enum 相关工具类

JDK5.0 中在增加 Enum 类的同时,也增加了两个工具类 EnumSet 和 EnumMap,这两个类都放在 java.util 包中。EnumSet 是一个针对枚举类型的高性能的 Set 接口实现。EnumSet 中装入的所有枚举对象都必须是同一种类型,在其内部,是通过 bit-vector 来实现,也就是通过一个 long 型数。EnumSet 支持在枚举类型的所有值的某个范围中进行迭代。回到上面日期枚举的例子上:

 enum WeekDayEnum { Mon, Tue, Wed, Thu, Fri, Sat, Sun }

你能够在每周七天日期中进行迭代,EnumSet 类提供一个静态方法 range 让迭代很容易完成:

 for(WeekDayEnum day : EnumSet.range(WeekDayEnum.Mon, WeekDayEnum.Fri)) { 
     System.out.println(day); 
 }

打印结果如下:

 Mon 
 Tue 
 Wed 
 Thu 
 Fri

EnumSet 还提供了很多个类型安全的获取子集的 of 方法,使你很容易取得子集:

 EnumSet<WeekDayEnum> subset = EnumSet.of(WeekDayEnum.Mon, WeekDayEnum.Wed); 
      for (WeekDayEnum day : subset) { 
          System.out.println(day);  
      }

打印结果如下:

 Mon 
 Wed

与 EnumSet 类似,EnumMap 也是一个高性能的 Map 接口实现,用来管理使用枚举类型作为 keys 的映射表,内部是通过数组方式来实现。EnumMap 将丰富的和安全的 Map 接口与数组快速访问结合到一起,如果你希望要将一个枚举类型映射到一个值,你应该使用 EnumMap。看下面的例子:

清单 5. EnumMap 示例
 // 定义一个 EnumMap 对象,映射表主键是日期枚举类型,值是颜色枚举类型
 private static Map<WeekDayEnum, RainbowColor> schema = 
            new EnumMap<WeekDayEnum, RainbowColor>(WeekDayEnum.class); 
    
 static{ 
    // 将一周的每一天与彩虹的某一种色彩映射起来
    for (int i = 0; i < WeekDayEnum.values().length; i++) { 
        schema.put(WeekDayEnum.values()[i], RainbowColor.values()[i]); 
    } 
 } 
 System.out.println("What is the lucky color today?"); 
 System.out.println("It's " + schema.get(WeekDayEnum.Sat));

当你询问周六的幸运色彩时候,会得到蓝色:

清单 6. 运行结果
 What is the lucky color today? 
 It's BLUE

结束语

Enum 类型提出给 JAVA 编程带了了极大的便利,让程序的控制更加的容易,也不容易出现错误。所以在遇到需要控制程序流程时候,可以多想想是否可以利用 enum 来实现。

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/dac55300424/article/details/24579113

智能推荐

oracle 12c 集群安装后的检查_12c查看crs状态-程序员宅基地

文章浏览阅读1.6k次。安装配置gi、安装数据库软件、dbca建库见下:http://blog.csdn.net/kadwf123/article/details/784299611、检查集群节点及状态:[root@rac2 ~]# olsnodes -srac1 Activerac2 Activerac3 Activerac4 Active[root@rac2 ~]_12c查看crs状态

解决jupyter notebook无法找到虚拟环境的问题_jupyter没有pytorch环境-程序员宅基地

文章浏览阅读1.3w次,点赞45次,收藏99次。我个人用的是anaconda3的一个python集成环境,自带jupyter notebook,但在我打开jupyter notebook界面后,却找不到对应的虚拟环境,原来是jupyter notebook只是通用于下载anaconda时自带的环境,其他环境要想使用必须手动下载一些库:1.首先进入到自己创建的虚拟环境(pytorch是虚拟环境的名字)activate pytorch2.在该环境下下载这个库conda install ipykernelconda install nb__jupyter没有pytorch环境

国内安装scoop的保姆教程_scoop-cn-程序员宅基地

文章浏览阅读5.2k次,点赞19次,收藏28次。选择scoop纯属意外,也是无奈,因为电脑用户被锁了管理员权限,所有exe安装程序都无法安装,只可以用绿色软件,最后被我发现scoop,省去了到处下载XXX绿色版的烦恼,当然scoop里需要管理员权限的软件也跟我无缘了(譬如everything)。推荐添加dorado这个bucket镜像,里面很多中文软件,但是部分国外的软件下载地址在github,可能无法下载。以上两个是官方bucket的国内镜像,所有软件建议优先从这里下载。上面可以看到很多bucket以及软件数。如果官网登陆不了可以试一下以下方式。_scoop-cn

Element ui colorpicker在Vue中的使用_vue el-color-picker-程序员宅基地

文章浏览阅读4.5k次,点赞2次,收藏3次。首先要有一个color-picker组件 <el-color-picker v-model="headcolor"></el-color-picker>在data里面data() { return {headcolor: ’ #278add ’ //这里可以选择一个默认的颜色} }然后在你想要改变颜色的地方用v-bind绑定就好了,例如:这里的:sty..._vue el-color-picker

迅为iTOP-4412精英版之烧写内核移植后的镜像_exynos 4412 刷机-程序员宅基地

文章浏览阅读640次。基于芯片日益增长的问题,所以内核开发者们引入了新的方法,就是在内核中只保留函数,而数据则不包含,由用户(应用程序员)自己把数据按照规定的格式编写,并放在约定的地方,为了不占用过多的内存,还要求数据以根精简的方式编写。boot启动时,传参给内核,告诉内核设备树文件和kernel的位置,内核启动时根据地址去找到设备树文件,再利用专用的编译器去反编译dtb文件,将dtb还原成数据结构,以供驱动的函数去调用。firmware是三星的一个固件的设备信息,因为找不到固件,所以内核启动不成功。_exynos 4412 刷机

Linux系统配置jdk_linux配置jdk-程序员宅基地

文章浏览阅读2w次,点赞24次,收藏42次。Linux系统配置jdkLinux学习教程,Linux入门教程(超详细)_linux配置jdk

随便推点

matlab(4):特殊符号的输入_matlab微米怎么输入-程序员宅基地

文章浏览阅读3.3k次,点赞5次,收藏19次。xlabel('\delta');ylabel('AUC');具体符号的对照表参照下图:_matlab微米怎么输入

C语言程序设计-文件(打开与关闭、顺序、二进制读写)-程序员宅基地

文章浏览阅读119次。顺序读写指的是按照文件中数据的顺序进行读取或写入。对于文本文件,可以使用fgets、fputs、fscanf、fprintf等函数进行顺序读写。在C语言中,对文件的操作通常涉及文件的打开、读写以及关闭。文件的打开使用fopen函数,而关闭则使用fclose函数。在C语言中,可以使用fread和fwrite函数进行二进制读写。‍ Biaoge 于2024-03-09 23:51发布 阅读量:7 ️文章类型:【 C语言程序设计 】在C语言中,用于打开文件的函数是____,用于关闭文件的函数是____。

Touchdesigner自学笔记之三_touchdesigner怎么让一个模型跟着鼠标移动-程序员宅基地

文章浏览阅读3.4k次,点赞2次,收藏13次。跟随鼠标移动的粒子以grid(SOP)为partical(SOP)的资源模板,调整后连接【Geo组合+point spirit(MAT)】,在连接【feedback组合】适当调整。影响粒子动态的节点【metaball(SOP)+force(SOP)】添加mouse in(CHOP)鼠标位置到metaball的坐标,实现鼠标影响。..._touchdesigner怎么让一个模型跟着鼠标移动

【附源码】基于java的校园停车场管理系统的设计与实现61m0e9计算机毕设SSM_基于java技术的停车场管理系统实现与设计-程序员宅基地

文章浏览阅读178次。项目运行环境配置:Jdk1.8 + Tomcat7.0 + Mysql + HBuilderX(Webstorm也行)+ Eclispe(IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持)。项目技术:Springboot + mybatis + Maven +mysql5.7或8.0+html+css+js等等组成,B/S模式 + Maven管理等等。环境需要1.运行环境:最好是java jdk 1.8,我们在这个平台上运行的。其他版本理论上也可以。_基于java技术的停车场管理系统实现与设计

Android系统播放器MediaPlayer源码分析_android多媒体播放源码分析 时序图-程序员宅基地

文章浏览阅读3.5k次。前言对于MediaPlayer播放器的源码分析内容相对来说比较多,会从Java-&amp;amp;gt;Jni-&amp;amp;gt;C/C++慢慢分析,后面会慢慢更新。另外,博客只作为自己学习记录的一种方式,对于其他的不过多的评论。MediaPlayerDemopublic class MainActivity extends AppCompatActivity implements SurfaceHolder.Cal..._android多媒体播放源码分析 时序图

java 数据结构与算法 ——快速排序法-程序员宅基地

文章浏览阅读2.4k次,点赞41次,收藏13次。java 数据结构与算法 ——快速排序法_快速排序法