。
一、 枚举
枚举类型(Enumerated Type) 很早就出现在编程语言中,它被用来将一组类似的值包含到一种类型当中。枚举看起来像全局常量,但是枚举可以更好地约束选择范围,让准确的数据。
枚举的简单分类
枚举可以简单
- 简单类型
- 定制类型
枚举的好处
1、枚举可以更好地约束选择范围,让准确的数据。
2、引入枚举类型到底能够给我们开发带来什么样好处呢?一个最直接的益处就是扩大 switch 语句使用范围。5.0 之前,Java 中 switch 的值只能够是简单类型,比如 int、byte、short、char, 有了枚举类型之后,就可以使用对象了。这样一来,程序的控制选择就变得更加的方便。
比如我们要定义一组颜色选择,限定红,绿,黑,黄四个颜色
如果卸载一个类,在里面列举颜色
Public static class RainbowColor {
// 红橙黄绿青蓝紫七种颜色的常量定义
public static final int RED = 0;
public static final int GREEN = 1;
public static final int YELLOW = 2;
public static final int BLANK = 3;
}
这样有隐患,不好
1、写方法的时候传入类型是int,别人随便传一个数字都可以,容易出错
2、不利于他人阅读,不利于后期维护
等等
枚举简单例子(简单类型)
public class SimpleEnum {
public enum MyColor {
RED, GREEN, BLANK, YELLOW
}
public static void main(String[] args) {
SimpleEnum simpleEnum = new SimpleEnum();
simpleEnum.showColor(MyColor.GREEN);
}
void showColor(MyColor color){
switch (color) {
case RED:
System.out.println("红色");
break;
case GREEN:
System.out.println("绿色");
break;
case BLANK:
System.out.println("黑色");
break;
case YELLOW:
System.out.println("黄色");
break;
default:
System.out.println("error");
break;
}
}
}
输出
绿色
对于这些枚举的颜色,JVM 都会在运行期构造成出一个简单的对象实例一一对应。这些对象都有唯一的 identity,类似整形数值一样,switch 语句就根据此来进行执行跳转。
获取枚举全部数据数组
我们要得到枚举里面的全部元素,可以通过枚举.values ,得到一个数组,比如
MyColor[] values = MyColor.values();
枚举的定制
定制类型的枚举
可以通过类似 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;
}
}
二、枚举的注意点
1、枚举的原理:Java中Enum的本质其实是在编译时期转换成对应的类的形式,枚举的常量值本质就是枚举对象
2、枚举的继承:继承自java.lang.enum,枚举会被标记称为final,所以不能再继承其他类,但是枚举可以实现接口
interface IShow{
String showColor();
}
enum TestColor implements IShow{
RED{
@Override
public String showColor() { // //每个对象都要覆写好接口中的方法。
return "红色" ;
}
},
GREED{
@Override
public String showColor() {
return "绿色" ;
}
},
BLUE{
@Override
public String showColor() {
return "蓝色" ;
}
};
}
public class EnumTest {
public static void main(String[] args) {
for(TestColor color:TestColor.values()){
System.out.println("====="+color.showColor());
}
}
}
输出:
=====红色
=====绿色
=====蓝色
3、enum 类型不支持 public 和 protected 修饰符的构造方法,因此枚举构造函数一定要是 private 或 friendly 的。也正因为如此,所以枚举对象是无法在程序中通过直接调用其构造方法来初始化的。
4、定义 enum 类型时候,如果是简单类型,那么最后一个枚举值后不用跟任何一个符号;但如果有定制方法,那么最后一个枚举值与后面代码要用分号';'隔开,不能用逗号或空格。
- 5、由于 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 与比较相关的方法,防止出现值比较方面的错误。
三、Enum工具类
- EnumSet
- EnumMap
JDK5.0 中在增加 Enum 类的同时,也增加了两个工具类 EnumSet 和 EnumMap,这两个类都放在 java.util 包中。
EnumSet
- EnumSet.range(from, to) 指定范围
- EnumSet.of(from, to) 获取子集
EnumSet 是一个针对枚举类型的高性能的 Set 接口实现。EnumSet 中装入的所有枚举对象都必须是同一种类型,在其内部,是通过 bit-vector 来实现,也就是通过一个 long 型数。EnumSet 支持在枚举类型的所有值的某个范围中进行循环。
示例代码:
import java.util.EnumSet;
public class SimpleEnum {
public enum MyColor {
RED, GREEN, BLANK, YELLOW
}
public static void main(String[] args) {
SimpleEnum simpleEnum = new SimpleEnum();
// EnumSet.range(from, to) //在哪一个范围里面循环
for(MyColor color : EnumSet.range(MyColor.GREEN, MyColor.YELLOW)){
System.out.println(color);
}
System.out.println("========");
for(MyColor color : EnumSet.range(MyColor.RED, MyColor.YELLOW)){
System.out.println(color);
}
System.out.println("========");
for(MyColor color : EnumSet.range(MyColor.RED, MyColor.YELLOW)){
if(color == simpleEnum.getColor()){
System.out.println("匹配到相关颜色 "+color);
}
}
}
public MyColor getColor(){
return MyColor.YELLOW;
}
}
输出:
GREEN
BLANK
YELLOW
========
RED
GREEN
BLANK
YELLOW
========
匹配到相关颜色 YELLOW
EnumSet 还提供了很多个类型安全的获取子集的 of 方法,使你很容易取得子集:
EnumSet.of 方法示例
import java.util.EnumSet;
public class SimpleEnum {
public enum MyColor {
RED, GREEN, BLANK, YELLOW
}
public static void main(String[] args) {
SimpleEnum simpleEnum = new SimpleEnum();
EnumSet<MyColor> myColorsSet = EnumSet.of(MyColor.RED, MyColor.GREEN);
MyColor testColor = simpleEnum.getColor();
for(MyColor color : myColorsSet){
System.out.println("集合颜色列举 "+color);
}
System.out.println("==========");
if(myColorsSet.contains(testColor)){
System.out.println("匹配到相关颜色 "+testColor);
}else{
System.out.println("无法 匹配颜色 "+testColor);
}
}
public MyColor getColor(){
return MyColor.YELLOW;
}
}
输出:
集合颜色列举 RED
集合颜色列举 GREEN
==========
无法 匹配颜色 YELLOW
EnumMap
EnumMap 是一个高性能的 Map 接口实现,用来管理使用枚举类型作为 keys 的映射表,内部是通过数组方式来实现。EnumMap 将丰富的和安全的 Map 接口与数组快速访问结合到一起,如果你希望要将一个枚举类型映射到一个值,你应该使用 EnumMap。
示例代码
import java.util.EnumMap;
import java.util.Map;
public class SimpleEnum {
public enum MyColor {
RED, GREEN, BLANK, YELLOW
}
public enum SeaSon {
Spring, Summer, Fall, Winter
}
static Map<SeaSon, MyColor> schema = new EnumMap<SeaSon, MyColor>(SeaSon.class);
{
for (int i = 0; i < MyColor.values().length; i++) {
schema.put(SeaSon.values()[i], MyColor.values()[i]);
}
}
public static void main(String[] args) {
SimpleEnum simpleEnum = new SimpleEnum();
for(Map.Entry<SeaSon, MyColor> entry : schema.entrySet()){
System.out.println("季节:"+entry.getKey()+"==== 颜色:"+entry.getValue());
}
}
public MyColor getColor() {
return MyColor.YELLOW;
}
}
输出:
季节:Spring==== 颜色:RED
季节:Summer==== 颜色:GREEN
季节:Fall==== 颜色:BLANK
季节:Winter==== 颜色:YELLOW
.
.
参考: