`

【转】Java 语言中 Enum 类型的使用介绍

阅读更多

Enum 类型的介绍


枚举类型(Enumerated Type) 很早就出现在编程语言中,它被用来将一组类似的值包含到一种类型当中。而这种枚举类型的名称则会被定义成独一无二的类型描述符,在这一点上和常量的定义相似。不过相比较常量类型,枚举类型可以为申明的变量提供更大的取值范围。
举个例子来说明一下,如果希望为彩虹描绘出七种颜色,你可以在 Java 程序中通过常量定义方式来实现。


清单 1. 常量定义

Java代码 
  1.             
  2. Public static class RainbowColor {
  3.   
  4.   // 红橙黄绿青蓝紫七种颜色的常量定义
  5.   public static final int RED = 0;
  6.   public static final int ORANGE = 1;
  7.   public static final int YELLOW = 2;
  8.   public static final int GREEN = 3;
  9.   public static final int CYAN = 4;
  10.   public static final int BLUE = 5;
  11.   public static final int PURPLE = 6;
  12. }

使用的时候,你可以在程序中直接引用这些常量。但是,这种方式还是存在着一些问题。
1.类型不安全由于颜色常量的对应值是整数形,所以程序执行过程中很有可能给颜色变量传入一个任意的整数值,导致出现错误。2.没有命名空间由于颜色常量只是类的属性,当你使用的时候不得不通过类来访问。
3.一致性差因为整形枚举属于编译期常量,所以编译过程完成后,所有客户端和服务器端引用的地方,会直接将整数值写入。这样,当你修改旧的枚举整数值后或者增加新的枚举值后,所有引用地方代码都需要重新编译,否则运行时刻就会出现错误。
4.类型无指意性由于颜色枚举值仅仅是一些无任何含义的整数值,如果在运行期调试时候,你就会发现日志中有很多魔术数字,但除了程序员本身,其他人很难明白其奥秘。

如何定义 Enum 类型

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

Java代码 
  1. 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 类型

Java代码 
  1. // 定义一周七天的枚举类型        
  2. public enum WeekDayEnum { Mon, Tue, Wed, Thu, Fri, Sat, Sun }
  3. // 读取当天的信息
  4. WeekDayEnum today = readToday();
  5. // 根据日期来选择进行活动
  6. switch(today) {
  7.   Mon: do something; break;
  8.   Tue: do something; break;
  9.   Wed: do something; break;
  10.   Thu: do something; break;
  11.   Fri: do something; break;
  12.   Sat: play sports game; break;
  13.   Sun: have a rest; break;
  14. }

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

如何定制 Enum 类型


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

清单 3. 定制枚举类型

Java代码 
  1. // 定义 RSS(Really Simple Syndication) 种子的枚举类型
  2. public enum NewsRSSFeedEnum {
  3.   // 雅虎头条新闻 RSS 种子
  4.   YAHOO_TOP_STORIES("http://rss.news.yahoo.com/rss/topstories"),
  5.   
  6.   //CBS 头条新闻 RSS 种子
  7.   CBS_TOP_STORIES("http://feeds.cbsnews.com/CBSNewsMain?format=xml"),
  8.   
  9.   // 洛杉矶时报头条新闻 RSS 种子
  10.   LATIMES_TOP_STORIES("http://feeds.latimes.com/latimes/news?format=xml");
  11.     
  12.   // 枚举对象的 RSS 地址的属性
  13.   private String rss_url;
  14.     
  15.   // 枚举对象构造函数
  16.   private NewsRSSFeedEnum(String rss) {
  17.     this.rss_url = rss;
  18.   }
  19.     
  20.   // 枚举对象获取 RSS 地址的方法
  21.   public String getRssURL() {
  22.     return this.rss_url;
  23.   }
  24. }

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

 

如何避免错误使用 Enum

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


清单 4. 避免错误使用 Enum 示例

Java代码 
  1. // 定义一个一周七天的枚举类型
  2. package example.enumeration.codes;
  3. public enum WeekDayEnum {
  4. Mon(1), Tue(2), Wed(3), Thu(4), Fri(5), Sat(6), Sun(7);
  5. private int index;
  6. WeekDayEnum(int idx) {
  7. this.index = idx;
  8. }
  9. public int getIndex() {
  10. return index;
  11. }
  12. }
  13. // 客户端程序,将一个枚举值通过网络传递给服务器端
  14. package example.enumeration.codes;
  15. import java.io.IOException;
  16. import java.io.ObjectOutputStream;
  17. import java.io.OutputStream;
  18. import java.net.InetSocketAddress;
  19. import java.net.Socket;
  20. import java.net.UnknownHostException;
  21. public class EnumerationClient {
  22. public static void main(String... args) throws UnknownHostException, IOException {
  23. Socket socket = new Socket();
  24. // 建立到服务器端的连接
  25. socket.connect(new InetSocketAddress("127.0.0.1"8999));
  26. // 从连接中得到输出流
  27. OutputStream os = socket.getOutputStream();
  28. ObjectOutputStream oos = new ObjectOutputStream(os);
  29. // 将星期五这个枚举值传递给服务器端
  30. oos.writeObject(WeekDayEnum.Fri);
  31. oos.close();
  32. os.close();
  33. socket.close();
  34. }
  35. }
  36. // 服务器端程序,将从客户端收到的枚举值应用到逻辑处理中
  37. package example.enumeration.codes;
  38. import java.io.*;
  39. import java.net.ServerSocket;
  40. import java.net.Socket;
  41. public class EnumerationServer {
  42. public static void main(String... args) throws IOException, ClassNotFoundException {
  43. ServerSocket server = new ServerSocket(8999);
  44. // 建立服务器端的网络连接侦听
  45. Socket socket = server.accept();
  46. // 从连接中获取输入流
  47. InputStream is = socket.getInputStream();
  48. ObjectInputStream ois = new ObjectInputStream(is);
  49. // 读出客户端传递来的枚举值
  50. WeekDayEnum day = (WeekDayEnum) ois.readObject();
  51. // 用值比较方式来对比枚举对象
  52. if (day == WeekDayEnum.Fri) {
  53. System.out.println("client Friday enum value is same as server's");
  54. else if (day.equals(WeekDayEnum.Fri)) {
  55. System.out.println("client Friday enum value is equal to server's");
  56. else {
  57. System.out.println("client Friday enum value is not same as server's");
  58. }
  59. // 用 switch 方式来比较枚举对象
  60. switch (day) {
  61. case Mon:
  62. System.out.println("Do Monday work");
  63. break;
  64. case Tue:
  65. System.out.println("Do Tuesday work");
  66. break;
  67. case Wed:
  68. System.out.println("Do Wednesday work");
  69. break;
  70. case Thu:
  71. System.out.println("Do Thursday work");
  72. break;
  73. case Fri:
  74. System.out.println("Do Friday work");
  75. break;
  76. case Sat:
  77. System.out.println("Do Saturday work");
  78. break;
  79. case Sun:
  80. System.out.println("Do Sunday work");
  81. break;
  82. default:
  83. System.out.println("I don't know which is day");
  84. break;
  85. }
  86. ois.close();
  87. is.close();
  88. socket.close();
  89. }
  90. }

打印结果如下:

Java代码 
  1. client Friday enum value is same as server's
  2. Do Friday work

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

 

Enum 相关工具类

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


Java代码 
  1. enum WeekDayEnum { Mon, Tue, Wed, Thu, Fri, Sat, Sun }

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

Java代码 
  1. for(WeekDayEnum day : EnumSet.range(WeekDayEnum.Mon, WeekDayEnum.Fri)) {
  2.   System.out.println(day);
  3. }

打印结果如下:


Java代码 
  1. Mon
  2. Tue
  3. Wed
  4. Thu
  5. Fri

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


Java代码 
  1. EnumSet<WeekDayEnum> subset = EnumSet.of(WeekDayEnum.Mon, WeekDayEnum.Wed);
  2.   for (WeekDayEnum day : subset) {
  3.     System.out.println(day);  
  4.   }

打印结果如下:

Java代码 
  1. Mon
  2. Wed

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

Java代码 
  1. // 定义一个 EnumMap 对象,映射表主键是日期枚举类型,值是颜色枚举类型
  2. private static Map<WeekDayEnum, RainbowColor> schema =
  3.     new EnumMap<WeekDayEnum, RainbowColor>(WeekDayEnum.class);
  4.   
  5. static{
  6.   // 将一周的每一天与彩虹的某一种色彩映射起来
  7.   for (int i = 0; i < WeekDayEnum.values().length; i++) {
  8.     schema.put(WeekDayEnum.values(), RainbowColor.values());
  9.   }
  10. }
  11. System.out.println("What is the lucky color today?");
  12. System.out.println("It's " + schema.get(WeekDayEnum.Sat));

当你询问周六的幸运色彩时候,会得到蓝色:清单 6. 运行结果

Java代码 
  1. What is the lucky color today?
  2. It's BLUE

结束语

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


关于作者

刘进目前在 IBM STG 云计算团队从事云管理平台产品开发,拥有十年的 Java 应用开发经验,对 J2SE 和 J2EE 的相关应用开发比较熟悉。

文章来源:developerworks

分享到:
评论

相关推荐

    Java Enum使用Demo源码

    博文“Java 语言中 Enum 类型的使用介绍”的源码。

    Java中的“枚举类型

    我想几乎每一个学习Java语言的人在学习枚举的时候首先的一个疑问就是“为什么不用static final字段替代?”,有这个疑问就说明你已经摸索到了枚举类型的最主要功能......

    java编程常用英语单词解释

    28. Enum (关键字):枚举的意思,是 Java 中的一种关键字,用于定义枚举类型。 29. Exception (java):例外、异常的意思,是 Java 中的一种基本概念,用于处理异常情况。 30. Execute (v.):执行的意思,用于描述...

    Java-2实用教程(第5版)习题解答.doc

    关键字是 Java 语言中已经被赋予特定意义的一些单词,不可以把关键字作为名字来用。true 和 false 不是关键字。一些常用的关键字有 class、implements、interface、enum、extends、abstract 等。 Java 语言的基本...

    java语言程序设计 java编程笔记 由浅入深的笔记 共32份 全套资源.rar

    java命名和数据类型.docx java序列化.docx Math类.docx Object(对象).docx operator(运算符).docx Properties.docx return语句的例子.docx Scanner和if语法.docx static.docx 毕向东视频的笔记.docx 参数传递....

    JAVA--达内培训笔记

    java中没有sizeof这个关键字了,java中的boolean类型的值只能用true和false,且这两个也是关键字 enum 枚举 assert 断言 一个标识符尽量符合语义信息,提高程序可读性 类 名 :每个单词首字母大写, 变量和...

    Java JDK 7学习笔记(国内第一本Java 7,前期版本累计销量5万册)

    1.1 java不只是语言 2 1.1.1 前世今生 2 1.1.2 三大平台 5 1.1.3 jcp与jsr 6 1.1.4 建议的学习路径 7 1.2 jvm/jre/jdk 11 1.2.1 什么是jvm 11 1.2.2 区分jre与jdk 14 1.2.3 下载、安装jdk 15 ...

    Java开发技术大全 电子版

    第2章Java语言基础19 2.1Java语言的特点19 2.2Java程序的构成21 2.3数据类 型23 2.3.1基本数据类型23 2.3.2常量25 2.3.3变量26 2.3.4整型数据27 .2.3.5浮点型数据29 2.3.6字符型数据30 2.3.7布尔型数据32 ...

    JAVA面向对象详细资料

    Java面向对象 1 1 学习方法与要求 1 2 面向对象语言与面向过程语言的区别 7 3 面向对象?什么对象? 8 4 什么是类? 9 5 如何创建一个类Class? 10 6 如何使用类创建对象 10 7 引用与实例 11 8 实例属性与实例方法 ...

    积分java源码-pyginny:PyGinny是一个生成跨语言类型声明和接口绑定的工具

    是一个生成跨语言类型声明和接口绑定的工具。 它旨在将 C++ 与 Java 或 Objective-C 连接起来。 Python 支持在python分支上的实验版本中可用。 PyGinny 可用于在 Android 和 iOS 上将跨平台 C++ 库代码与特定于平台...

    C++大学教程,一本适合初学者的入门教材(part2)

    7.11 有关对象的思考:在电梯模拟程序中使用复合和动态对象管理 小结 术语 自测练习 自测练习答案 练习 第8章 运算符重载 8.1 简介 8.2 运算符重载的基础 8.3 运算符重载的限制 8.4 用作类成员与友元函数的...

    fastjson.jar

    Fastjson是一个Java语言编写的JSON处理器。 1、遵循http://json.org标准,为其官方网站收录的参考实现之一。 2、功能qiang打,支持JDK的各种类型,包括基本的JavaBean、Collection、Map、Date、Enum、泛型。 3、无...

    RubyEnum:ruby 的 java 风格枚举

    当我们有 ruby​​ 时,为什么要使用像 java 这样可怕的语言中的模式? 因为枚举很棒。 任何时候你有多种类型的对象,这些类型只是分类,而不是对象本身,你可以使用枚举。 例如,如果您过去有一个具有角色属性...

    fastjson解析器和生成器-其他

    fastjson是一个性能很好的Java语言实现的JSON解析器和生成器,来自阿里巴巴的工程师开发。 Fastjson特点: 在服务器端和android客户端中提供最佳性能 提供简单的toJSONString()和parseObject()方法,将Java对象...

    fastjson解析jar包

    fastjson解析jar包,是一个Java语言编写的JSON处理器,由阿里巴巴公司开发。 、1、遵循http://json.org标准,为其官方网站收录的参考实现之一。 2、功能qiang打,支持JDK的各种类型,包括基本的JavaBean、Collection...

    fastjson-1.2.3.jar

    Fastjson是一个Java语言编写的高性能功能完善的JSON库。fastjson采用独创的算法,将parse的速度提升到极致,超过所有json库,包括曾经号称最快的jackson。并且还超越了google的二进制协议protocol buf。Fastjson完全...

    fastjson的jar包,包含api文档

    Fastjson是一个Java语言编写的高性能功能完善的JSON库。 高性能: fastjson采用独创的算法,将parse的速度提升到极致,超过所有json库,包括曾经号称最快的jackson。并且还超越了google的二进制协议protocol buf。 ...

    java版斗地主源码-otsoc:https://github.com/rcore-os/rCore/wiki/os-tutorial-sum

    rust的错误处理把enum写成返回值,并不把错误当成别的什么东西,感觉挺有趣,但写起来不方便(一定要处理错误,不处理要像java一样一直向上传,当然这样的确安全) 这个match貌似只是代替了频繁的if-else + ...

    在一小时内学会 C#(txt版本)

    引用类型在堆(heap)中分配内存且当其不再使用时,将自动进行垃圾清理。和 C++ 要求用户显示创建 delete 运算符不一样,它们使用新运算符创建,且没有 delete 运算符。在 C# 中它们自动由垃圾回收系统回收。 引用...

    Json解析工具之fastJson

    Fastjson是一个Java语言编写的JSON处理器。 1、遵循http://json.org标准,为其官方网站收录的参考实现之一。 2、功能qiang打,支持JDK的各种类型,包括基本的JavaBean、Collection、Map、Date、Enum、泛型。 3、无...

Global site tag (gtag.js) - Google Analytics