关于对Enum的理解

之前一直对枚举类型的理解存在误解,现重新学习

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、byte、short、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 地址的方法  @Override
    public String getRssURL() {
        return this.rss_url;
    }
 }

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

如何避免错误使用 Enum

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

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

看下面的这个例子:

 1 package com.csdn.myEnum;
 2
 3 import java.util.EnumMap;
 4 import java.util.EnumSet;
 5
 6 public class LightTest {
 7
 8     // 1. 定义枚举类型
 9     public enum Light {
10        // 利用构造函数传参
11        RED (1), GREEN (3), YELLOW (2);
12
13        // 定义私有变量
14        private int nCode ;
15
16        // 构造函数,枚举类型只能为私有
17        private Light( int _nCode) {
18            this . nCode = _nCode;
19        }
20
21        @Override
22        public String toString() {
23            return String.valueOf ( this . nCode );
24        }
25     }
26
27     /**
28       * @param args
29       */
30     public static void main(String[] args ) {
31
32        // 1. 遍历枚举类型
33        System. out .println( " 演示枚举类型的遍历 ......" );
34        testTraversalEnum ();
35
36        // 2. 演示 EnumMap 对象的使用
37        System. out .println( " 演示 EnmuMap 对象的使用和遍历 ....." );
38        testEnumMap ();
39
40        // 3. 演示 EnmuSet 的使用
41        System. out .println( " 演示 EnmuSet 对象的使用和遍历 ....." );
42        testEnumSet ();
43     }
44
45     /**
46       * 演示枚举类型的遍历
47       */
48     private static void testTraversalEnum() {
49        Light[] allLight = Light.values ();
50        for (Light aLight : allLight) {
51            System. out .println( " 当前灯 name : " + aLight.name());
52            System. out .println( " 当前灯 ordinal : " + aLight.ordinal());
53            System. out .println( " 当前灯: " + aLight);
54        }
55     }
56
57     /**
58       * 演示 EnumMap 的使用, EnumMap 跟 HashMap 的使用差不多,只不过 key 要是枚举类型
59       */
60     private static void testEnumMap() {
61        // 1. 演示定义 EnumMap 对象, EnumMap 对象的构造函数需要参数传入 , 默认是 key 的类的类型
62        EnumMap<Light, String> currEnumMap = new EnumMap<Light, String>(
63               Light. class );
64        currEnumMap.put(Light. RED , " 红灯 " );
65        currEnumMap.put(Light. GREEN , " 绿灯 " );
66        currEnumMap.put(Light. YELLOW , " 黄灯 " );
67
68        // 2. 遍历对象
69        for (Light aLight : Light.values ()) {
70            System. out .println( "[key=" + aLight.name() + ",value="
71                   + currEnumMap.get(aLight) + "]" );
72        }
73     }
74
75     /**
76       * 演示 EnumSet 如何使用, EnumSet 是一个抽象类,获取一个类型的枚举类型内容 <BR/>
77       * 可以使用 allOf 方法
78       */
79     private static void testEnumSet() {
80        EnumSet<Light> currEnumSet = EnumSet.allOf (Light. class );
81        for (Light aLightSetElement : currEnumSet) {
82            System. out .println( " 当前 EnumSet 中数据为: " + aLightSetElement);
83        }
84
85     }
86 }

执行结果如下:


演示枚举类型的遍历 ......

当前灯 name : RED

当前灯 ordinal : 0

当前灯: 1

当前灯 name : GREEN

当前灯 ordinal : 1

当前灯: 3

当前灯 name : YELLOW

当前灯 ordinal : 2

当前灯: 2

演示 EnmuMap 对象的使用和遍历 .....

[key=RED,value= 红灯 ]

[key=GREEN,value= 绿灯 ]

[key=YELLOW,value= 黄灯 ]

演示 EnmuSet 对象的使用和遍历 .....

当前 EnumSet 中数据为: 1

当前 EnumSet 中数据为: 3

当前 EnumSet 中数据为: 2

四、   通常定义常量方法和枚举定义常量方法区别

以下内容可能有些无聊,但绝对值得一窥

1.    代码:

public class State {

public static final int ON = 1;

public static final Int OFF= 0;

}

有什么不好了,大家都这样用了很长时间了,没什么问题啊。

首先,它不是类型安全的。你必须确保是int

其次,你还要确保它的范围是0 和1

最后,很多时候你打印出来的时候,你只看到 1 和0 ,

但其没有看到代码的人并不知道你的企图,抛弃你所有旧的public static final 常量吧

2.    可以创建一个enum 类,把它看做一个普通的类。除了它不能继承其他类了。(java 是单继承,它已经继承了Enum),

可以添加其他方法,覆盖它本身的方法

3.    switch() 参数可以使用enum 了

4.    values() 方法是编译器插入到enum 定义中的static 方法,所以,当你将enum 实例向上转型为父类Enum是,values() 就不可访问了。解决办法:在Class 中有一个getEnumConstants() 方法,所以即便Enum 接口中没有values() 方法,我们仍然可以通过Class 对象取得所有的enum 实例

5.    无法从enum 继承子类,如果需要扩展enum 中的元素,在一个接口的内部,创建实现该接口的枚举,以此将元素进行分组。达到将枚举元素进行分组。

6.    使用EnumSet 代替标志。enum 要求其成员都是唯一的,但是enum 中不能删除添加元素。

7.    EnumMap 的key 是enum ,value 是任何其他Object 对象。

8.    enum 允许程序员为eunm 实例编写方法。所以可以为每个enum 实例赋予各自不同的行为。

9.    使用enum 的职责链(Chain of Responsibility) . 这个关系到设计模式的职责链模式。以多种不同的方法来解决一个问题。然后将他们链接在一起。当一个请求到来时,遍历这个链,直到链中的某个解决方案能够处理该请求。

10.   使用enum 的状态机

11.   使用enum 多路分发

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

参考资料

学习

附录自己的代码(比较前台输入的数据转换成后台存储的数据):

import java.util.EnumMap;

/**
 * Created by A0017282 on 2017/2/27.
 */
public class EnumDemoOne {
    public enum Light{
        衣服(1),包包(2),洗护(3),水果(4),蔬菜(5);

        private int colorValue;
        private Light(int colorValue){
            this.colorValue=colorValue;
        }

        public int getColorValue() {
            return colorValue;
        }

        public void setColorValue(int colorValue) {
            this.colorValue = colorValue;
        }

        @Override
        public String toString(){
            return String.valueOf(this.colorValue);
        }
    }

    public static void main(String[] args ) {
        Light[] allLight = Light.values();
        String testName = "包包";
        for (Light aLight : allLight){
            if(aLight.name().equals(testName)){
                System.out.println(aLight.colorValue);
            }

        }
    }
}

  结果输出为:2

时间: 2024-08-07 20:54:01

关于对Enum的理解的相关文章

深入理解Java注解类型(@Annotation)

"-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> 深入理解Java注解类型(@Annotation) - zejian的博客 - 博客频道 - CSDN.NET zejian的博客 目录视图 摘要视图 订阅 [活动]2017 CSDN博客专栏评选 &nbsp [5月书讯]流畅的Python,终于等到你!    &am

Java枚举类型理解

Enum格式理解 Enum的格式可以看做跟class关键字一样 class的定义格式如下: public class abc{ } enum的定义格式如下: Public enum abc { } 引用enum的地方 enum可以单独定义成一个枚举类 也可以作为类的某个字段 用来校验某些行为的种类,不在这些种类之中,就会报错 发现的问题 如果在case语句中调用了return,那么编译器就会抱怨缺少default语句了,验证发现和描述不符.19.3节 验证values的神秘之处时,提示错误“Ca

学习Enum

将Enum枚举理解为静态常量,规定好了这个类只能有这几种结果: public class EnumTest2 { public final static EnumTest2 CMCC=new EnumTest2(); public final static EnumTest2 CUCC=new EnumTest2(); public final static EnumTest2 CT=new EnumTest2(); } 那么我们可以这样来写: 1 public abstract class E

全面理解Java内存模型(JMM)及volatile关键字(转)

原文地址:全面理解Java内存模型(JMM)及volatile关键字 关联文章: 深入理解Java类型信息(Class对象)与反射机制 深入理解Java枚举类型(enum) 深入理解Java注解类型(@Annotation) 深入理解Java类加载器(ClassLoader) 深入理解Java并发之synchronized实现原理 Java并发编程-无锁CAS与Unsafe类及其并发包Atomic 深入理解Java内存模型(JMM)及volatile关键字 剖析基于并发AQS的重入锁(Reetr

深入剖析java并发之阻塞队列LinkedBlockingQueue与ArrayBlockingQueue

关联文章: 深入理解Java类型信息(Class对象)与反射机制 深入理解Java枚举类型(enum) 深入理解Java注解类型(@Annotation) 深入理解Java类加载器(ClassLoader) 深入理解Java并发之synchronized实现原理 Java并发编程-无锁CAS与Unsafe类及其并发包Atomic 深入理解Java内存模型(JMM)及volatile关键字 剖析基于并发AQS的重入锁(ReetrantLock)及其Condition实现原理 剖析基于并发AQS的共

java泛型小问题

几年前当Java5还未正式发布的时候,看到过一些人写的介绍Tiger中的新特性,当时对我第一感觉冲击最大的就是泛型(generics)和注释(annotation),因为它们直接影响了我们编码的语法习惯. 在后来的使用过程中,对于泛型一直没有特别深入的使用过,没有遇到那样的需求和场景.只需要了解Java中的泛型是编译期的,运行期被“擦拭”掉了:然后还有几种通配符的表示就足够了. 直到一天我在查看Java5中Enum的源代码时,发现它是这么定义的: Java代码 public abstract c

NBUT 1028 该减肥了(简单递推)

[1028] 该减肥了 时间限制: 1000 ms 内存限制: 65535 K 问题描述 由于长期缺乏运动,Teacher Xuan发现自己的身材臃肿了许多,于是他想健身,更准确地说是减肥.Teacher Xuan买来一块圆形的毯子,把它们分成三等分,分别标上A,B,C,称之为“跳舞毯”,他的运动方式是每次都从A开始跳,每次都可以任意跳到其他块,但最后必须跳回A,且不能原地跳.为达到减肥效果,Teacher Xuan每天都会坚持跳n次,有天他突然想知道当他跳n次时共几种跳法,结果想了好几天没想出

中秋佳节--理解Enum枚举

一.Enum枚举的作用 1.使用枚举可以限定取值范围,枚举中定义的每个常量都可以理解为对象: Eg: Public enum Color{ RED, GREEN,BULE; } 说明:RED实际上就表示的是枚举的名称,默认的编号是0,可以使用ordinal()方法获得. 2.使用enum关键字定义枚举类,其中包含的对象可以初始化定义(初始化构造函数) Eg: package cn.test.java.enums; enum ColorDemo{ RED("红色"),GREEN(&quo

深入理解Java枚举类型(enum)

[版权申明]未经博主同意,谢绝转载!(请尊重原创,博主保留追究权) http://blog.csdn.net/javazejian/article/details/71333103 出自[zejian的博客] 关联文章: 理解Java位运算 深入理解Java类型信息(Class对象)与反射机制 本篇主要是深入对Java中枚举类型进行分析,主要内容如下: 理解枚举类型 枚举的定义 枚举实现原理 枚举的常见方法 Enum抽象类常见方法 编译器生成的Values方法与ValueOf方法 枚举与Clas