Java中的枚举类型详解
枚举类型介绍
枚举类型(Enumerated Type) 很早就出现在编程语言中,它被用来将一组类似的值包含到一种类型当中。而这种枚举类型的名称则会被定义成独一无二的类型描述符,在这一点上和常量的定义相似。不过相比较常量类型,枚举类型可以为申明的变量提供更大的取值范围。
举个例子来说明一下,如果希望为彩虹描绘出七种颜色,你可以在 Java 程序中通过常量定义方式来实现。
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;
}
使用的时候,你可以在程序中直接引用这些常量。但是,这种方式还是存在着一些问题。
- 类型不安全
由于颜色常量的对应值是整数形,所以程序执行过程中很有可能给颜色变量传入一个任意的整数值,导致出现错误。
- 没有命名空间
由于颜色常量只是类的属性,当你使用的时候不得不通过类来访问。
- 一致性差
因为整形枚举属于编译期常量,所以编译过程完成后,所有客户端和服务器端引用的地方,会直接将整数值写入。这样,当你修改旧的枚举整数值后或者增加新的枚举值后,所有引用地方代码都需要重新编译,否则运行时刻就会出现错误。
- 类型无指意性
由于颜色枚举值仅仅是一些无任何含义的整数值,如果在运行期调试时候,你就会发现日志中有很多魔术数字,但除了程序员本身,其他人很难明白其奥秘。
如何定义枚举类型
为了改进 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, 有了枚举类型之后,就可以使用对象了。这样一来,程序的控制选择就变得更加的方便,看下面的例子:
// 定义一周七天的枚举类型
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 语句就根据此来进行执行跳转。
如何定制枚举类型
除了以上这种最常见的枚举定义形式外,如果需要给枚举类型增加一些复杂功能,也可以通过类似 class 的定义来给枚举进行定制。比如要给 enum 类型增加属性,可以像下面这样定义:
// 定义 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 类型不支持 public 和 protected 修饰符的构造方法,因此构造函数一定要是 private 或 friendly 的。也正因为如此,所以枚举对象是无法在程序中通过直接调用其构造方法来初始化的。
- 定义 enum 类型时候,如果是简单类型,那么最后一个枚举值后不用跟任何一个符号;但如果有定制方法,那么最后一个枚举值与后面代码要用分号';'隔开,不能用逗号或空格。
- 由于 enum 类型的值实际上是通过运行期构造出对象来表示的,所以在 cluster 环境下,每个虚拟机都会构造出一个同义的枚举对象。因而在做比较操作时候就需要注意,如果直接通过使用等号 ( ‘ == ’ ) 操作符,这些看似一样的枚举值一定不相等,因为这不是同一个对象实例。
看下面的这个例子:
// 定义一个一周七天的枚举类型
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 与比较相关的方法,防止出现值比较方面的错误。
枚举类型相关工具类
DK5.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
Wed
EnumSet 类似,EnumMap 也是一个高性能的 Map 接口实现,用来管理使用枚举类型作为 keys 的映射表,内部是通过数组方式来实现。EnumMap 将丰富的和安全的 Map 接口与数组快速访问结合到一起,如果你希望要将一个枚举类型映射到一个值,你应该使用 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));
当你询问周六的幸运色彩时候,会得到蓝色:
What is the lucky color today?
It's BLUE
Java中的枚举类型详解的更多相关文章
- Java中的枚举使用详解
转载至:http://www.cnblogs.com/linjiqin/archive/2011/02/11/1951632.html package com.ljq.test; /** * 枚举用法 ...
- java中的io系统详解 - ilibaba的专栏 - 博客频道 - CSDN.NET
java中的io系统详解 - ilibaba的专栏 - 博客频道 - CSDN.NET 亲,“社区之星”已经一周岁了! 社区福利快来领取免费参加MDCC大会机会哦 Tag功能介绍—我们 ...
- Java中的main()方法详解
在Java中,main()方法是Java应用程序的入口方法,也就是说,程序在运行的时候,第一个执行的方法就是main()方法,这个方法和其他的方法有很大的不同,比如方法的名字必须是main,方法必须是 ...
- Java I/O : Java中的进制详解
作者:李强强 上一篇,泥瓦匠基础地讲了下Java I/O : Bit Operation 位运算.这一讲,泥瓦匠带你走进Java中的进制详解. 一.引子 在Java世界里,99%的工作都是处理这高层. ...
- 全面解读Java中的枚举类型enum的使用
这篇文章主要介绍了Java中的枚举类型enum的使用,开始之前先讲解了枚举的用处,然后还举了枚举在操作数据库时的实例,需要的朋友可以参考下 关于枚举 大多数地方写的枚举都是给一个枚举然后例子就开始sw ...
- Java基础学习(五)-- Java中常用的工具类、枚举、Java中的单例模式之详解
Java中的常用类 1.Math : 位于java.lang包中 (1)Math.PI:返回一个最接近圆周率的 (2)Math.abs(-10):返回一个数的绝对值 (3)Math.cbrt(27): ...
- java枚举类型详解
枚举类型是JDK1.5的新特性.显然,enum很像特殊的class,实际上enum声明定义的类型就是一个类.而这些类都是类库中Enum类的子类(java.lang.Enum<E>).它 ...
- Java魔法堂:枚举类型详解
一.前言 Java的枚举类型相对C#来说具有更灵活可配置性,Java的枚举类型可以携带更多的信息. // C# enum MyColor{ RED = , BLUE = } Console.Write ...
- Java中JNI的使用详解第二篇:JNIEnv类型和jobject类型的解释
上一篇说的是一个简单的应用,说明JNI是怎么工作的,这一篇主要来说一下,那个本地方法sayHello的参数的说明,以及其中方法的使用 首先来看一下C++中的sayHello方法的实现: JNIEXPO ...
随机推荐
- 加密传输SSL协议3_非对称加密
困死了,这里对非对称加密体系开个头,具体的实验明天写 非对称加密体系 为了解决对称加密中密钥的传输的问题,一些天才的数学家就提出了非对称式的加密体系,也称为公钥加密体系. 加密和解密的密钥是不同的.一 ...
- 如果不知道MySQL当前使用配置文件(my.cnf)的路径的解决方法
如果不知道当前使用的配置文件的路径,可以尝试下面的操作: # which mysqld /usr/local/mysql/bin/mysqld # /usr/local/mysql/bin/mysql ...
- phpcms-v9 --- 如何通过{pc}标签获取全站文章内容?
1.phpcms-v9默认情况下只能根据catid获取当前栏目及子栏目下的文章,但是有时候我们需要如何通过{pc}标签来获取全站文章内容的需求,应该怎么做呢? 第一步:在content_tag.cla ...
- 我来讲讲在c#中怎么进行xml文件操作吧,主要是讲解增删改查!
我把我写的四种方法代码贴上来吧,照着写没啥问题. 注: <bookstore> <book> <Id>1</Id> <tate>2010-1 ...
- linux LVM 逻辑卷
fdisk pvcreate vgcreate lvcreate 查看显示 创建 删除 扩容 激活 扫描查找 LV lvdisplay lvcreate lvremove lvextend lvcha ...
- Python核心编程笔记---- print
在仅用变量名时,输出的字符串是用单引号括起来的.这个是为了让非字符串对象也可能以字符的形式显示在屏幕上. 而print 函数打印出来的是变量的值. print 调用的是str()方法.而仅用变量名时调 ...
- 低功耗之战!ANT VS Bluetooth LE
利用近距离无线通信技术将手机及可穿戴式传感器终端等与智能电话连接起来,实现新的功能.最近,以此为目标的行动正在展开.其中备受关注的近距离无线方式是“ANT”和“Bluetooth LE”.为了在各种便 ...
- JS获取年月日时分秒
var d = new Date(); ) + "-" + d.getDate() + " " + d.getHours() + ":" + ...
- ADB几种常见的错误及解决方法
下面列举出几种常见的错误及解决方法. Q1:无效的安装包,安装包已损坏[INSTALL_FAILED_INVALID_APK] A1:请检查安装包是否完整.如果是xpk包,可以通过 手动安装xpk来检 ...
- 解决ArcSDE图层名被占用的问题
点击文章查看 当你发现上面那篇文章时你已经接近成功了,但还差最后一步! <!- -delete from sde.GDB_OBJECTCLASSES where NAME = upper(' ...