Java1.5 中出现了枚举类型。当一个值都在一个固定的范围内变化,那就可以使用 enum 类型来定义。比如说,一周有七天,一年有四季。

没有枚举类的时候,我们用常量来定义一组范围值的:

public static class Season {
public static final int SPRING = 1;
public static final int SUMMER = 2;
public static final int AUTUMN = 3;
public static final int WINTER = 4;
}

通过常量定义的方式有这样几个缺点:

  1. 类型不安全。如 Season 类所示,程序执行过程中接收的是任意一个 int 类型的值,完全可能传入一个 1 到 4 之外的值,导致错误的出现。

  2. 一致性差。int 枚举属于编译期常量,编译完成后,代码中引用的地方会直接将整数值写入。也就是说,该 Int 枚举被修改之后,所有引用它的程序都需要重新编译。

  3. 类型无指意性。Seaon 枚举值仅仅是一些无任何含义的整数值,调试期间仅仅是一些魔数。

定义一个 enum

《阿里巴巴Java开发手册(华山版)》中建议我们这样定义一个 enum 类:

【参考】枚举类名带上 Enum 后缀,枚举成员名称需要全大写,单词间用下划线隔开。

说明:枚举其实就是特殊的类,域成员均为常量,且构造方法被默认强制是私有。

正例:枚举名字为 ProcessStatusEnum 的成员名称:SUCCESS / UNKNOWN_REASON。

【推荐】如果变量值仅在一个固定范围内变化用 enum 类型来定义。

说明:如果存在名称之外的延伸属性应使用 enum 类型,下面正例中的数字就是延伸信息,表示一年中的第几个季节。

由此,我们可以定义一个季节的枚举类,如下所示:

public enum SeasonEnum {
SPRING(1), SUMMER(2), AUTUMN(3), WINTER(4); private int seq; SeasonEnum(int seq) {
this.seq = seq;
} public int getSeq() {
return seq;
}
}

enum 类

使用 enum 定义的枚举类默认继承 java.lang.Enum

public abstract class Enum<E extends Enum<E>>
implements Comparable<E>, Serializable {
...
}

变量

// enum 实例的名字
private final String name;
// 定义enum实例的顺序,从0开始计数
private final int ordinal;

方法

// 返回enum实例的名称,eg. SeasonEnum.SPRING.name() - output: SPRING
public final String name() { return name;}
// 返回enum实例的顺序,eg. SeasonEnum.SPRING.ordinal() - output: 0
public final int ordinal() { return ordinal;}
// enum实例 -> 字符串 eg. SeasonEnum.SPRING.toString() - output: SPRING
public String toString() { return name;}
// 比较是不是一个constant,即两个enum实例的顺序(oridnal)是否相同
public final int compareTo(E o) { ... return self.ordinal - other.ordinal; }
// 比较是不是同一个对象
public final boolean equals(Object other) { return this==other;}
// 返回声明枚举常量的类的类对象
public final Class<E> getDeclaringClass(){...}
// 返回指定name的enum实例
public static <T extends Enum<T>> T valueOf(Class<T> enumType,String name) {} // values()是编译器产生的一个方法,API中没有,可以用来遍历
public static SeasonEnum getSeasonByOrdinal(int seq) {
for (SeasonEnum season: SeasonEnum.values()) {
if(season.getSeq() == seq) {
return season;
}
}
return null;
}

enum 的用法

用法一:定义常量

也就是最开始说的,enum 出现之后,用 enum 来代替常量类型。

public static class Season {
public static final int SPRING = 1;
public static final int SUMMER = 2;
public static final int AUTUMN = 3;
public static final int WINTER = 4;
} public enum SeasonEnum {
SPRING, SUMMER, AUTUMN, WINTER;
}

用法二:switch

Java1.5 推出了 enum,Java1.6 支持在 switch 中使用 enum 类型。


public enum OperationUseSwitch {
PLUS, MINUS, TIMES, DIVIDE; double apply(double x, double y) {
switch (this) {
case PLUS:
return x + y;
case MINUS:
return x - y;
case TIMES:
return x * y;
case DIVIDE:
return x / y;
}
// 如果this不属于上面四种操作符,抛出异常
throw new AssertionError("Unknown operation: " + this);
}
}

用法三:特定于常量的方法实现

特定于常量的方法实现指的是,在 enum 类中定义一个抽象方法,然后各个枚举常量需要实现这个方法。这样做的优点在于,相对于 switch 语句,抽象程度更高,每个 enum 实例都需要实现统一的方法,不会漏下。

public enum Operation {
PLUS {
double apply(double x, double y) {
return x + y;
}
},
MINUS {
double apply(double x, double y) {
return x - y;
}
},
TIMES {
double apply(double x, double y) {
return x * y;
}
},
DIVIDE {
double apply(double x, double y) {
return x / y;
}
}; abstract double apply(double x, double y);
}

用法四:实现接口

enum 类都隐式得继承自 java.lang.Enum,而 Java 只支持单继承,所有 enum 类不能再继承其它类,但是可以实现接口。比如用法三中将统一的方法抽象出来,就可以使用接口来实现:

public interface Behavior {
double apply(double x, double y);
} public enum Operation implements Behavior{
PLUS {
public double apply(double x, double y) {
return x + y;
}
},
MINUS {
public double apply(double x, double y) {
return x - y;
}
},
TIMES {
public double apply(double x, double y) {
return x * y;
}
},
DIVIDE {
public double apply(double x, double y) {
return x / y;
}
};
}

用法五:覆盖 Enum 的方法

public class Test {
public enum Color {
RED("红色", 1), GREEN("绿色", 2), BLANK("白色", 3), YELLO("黄色", 4);
// 成员变量
private String name;
private int index; // 构造方法
private Color(String name, int index) {
this.name = name;
this.index = index;
} // 覆盖方法
@Override
public String toString() {
return this.index + "_" + this.name;
}
} public static void main(String[] args) {
System.out.println(Color.RED.toString());
}
}

EnumSet 和 EnumMap

EnumSet 和 EnumMap 是两个为枚举而设计的集合。EnumSet保证集合中的元素不重复;EnumMap中的 key 是enum 类型,而 value 则可以是任意类型。

EnumMap

public class Herb {
public enum Type { ANNUAL, PERENNIAL, BIENNIAL } private final String name;
private final Type type; Herb(String name, Type type) {
this.name = name;
this.type = type;
} @Override public String toString() {
return name;
}
} public static void main(String[] args) {
    Herb[] garden = { new Herb("Basil", Type.ANNUAL),
            new Herb("Carroway", Type.BIENNIAL),
            new Herb("Dill", Type.ANNUAL),
            new Herb("Lavendar", Type.PERENNIAL),
            new Herb("Parsley", Type.BIENNIAL),
            new Herb("Rosemary", Type.PERENNIAL) };     // Using an EnumMap to associate data with an enum - Page 162
    Map<Herb.Type, Set<Herb>> herbsByType = new EnumMap<Herb.Type, Set<Herb>>(
            Herb.Type.class);
    for (Herb.Type t : Herb.Type.values())
        herbsByType.put(t, new HashSet<Herb>());
    for (Herb h : garden)
        herbsByType.get(h.type).add(h);
    System.out.println(herbsByType); }

EnumSet

EnumSet 是枚举类型的高性能 Set 实现,它要求放入它的枚举常量必须属于同一枚举类型。EnumSet 提供了许多工厂方法以便于初始化。

方法名称 描述
allOf(Class element type) 创建一个包含指定枚举类型中所有枚举成员的 EnumSet 对象
complementOf(EnumSet s) 创建一个与指定 EnumSet 对象 s 相同的枚举类型 EnumSet 对象, 并包含所有 s 中未包含的枚举成员
copyOf(EnumSet s) 创建一个与指定 EnumSet 对象 s 相同的枚举类型 EnumSet 对象, 并与 s 包含相同的枚举成员
noneOf(<Class elementType) 创建指定枚举类型的空 EnumSet 对象
of(E first,e...rest) 创建包含指定枚举成员的 EnumSet 对象
range(E from ,E to) 创建一个 EnumSet 对象,该对象包含了 from 到 to 之间的所有枚 举成员

EnumSet 作为 Set 接口实现,它支持对包含的枚举常量的遍历。

for(Operation op:EnumSet.range(Operation.PLUS,Operation.MULTIPLY)) {
doSomeThing(op);
}

参考文献

W3CSchool-Java枚举

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

Java 枚举(enum) 详解7种常见的用法

Java枚举 —— 很少被使用,或许是因为真正了解它的人太少了

Java枚举(enum)详解:Java声明枚举类型、枚举(enum)类、EnumMap 与 EnumSet

如何使用Java中的Enum类的更多相关文章

  1. 深入掌握Java中的enum

    对于要在程序中要表示有限种类的某事物,一般我们可以采用两种方式,一是使用:public static final String 常量:二是使用enum来表示.一般而言前者简单,但是不能够很好的提供更多 ...

  2. 带有静态方法的类(java中的math类)

    带有静态方法的类通常(虽然不一定是这样)不打算被初始化. 可以用私有构造函数来限制非抽象类被初始化. 例如,java中的math类.它让构造函数标记为私有,所以你无法创建Math的实例.但Math类却 ...

  3. java中的File类

    File类 java中的File类其实和文件并没有多大关系,它更像一个对文件路径描述的类.它即可以代表某个路径下的特定文件,也可以用来表示该路径的下的所有文件,所以我们不要被它的表象所迷惑.对文件的真 ...

  4. Java基础(43):Java中的Object类与其方法(转)

    Object类 java.lang.Object java.lang包在使用的时候无需显示导入,编译时由编译器自动导入. Object类是类层次结构的根,Java中所有的类从根本上都继承自这个类. O ...

  5. java中基于TaskEngine类封装实现定时任务

    主要包括如下几个类: 文章标题:java中基于TaskEngine类封装实现定时任务 文章地址: http://blog.csdn.net/5iasp/article/details/10950529 ...

  6. Java中的Unsafe类111

    1.Unsafe类介绍 Unsafe类是在sun.misc包下,不属于Java标准.但是很多Java的基础类库,包括一些被广泛使用的高性能开发库都是基于Unsafe类开发的,比如Netty.Hadoo ...

  7. Java中遍历实体类(处理MongoDB)

    在实际过程中,经常要将实体类进行封装,尤其是处理数据库的过程中:因此,对于遍历实体类能够与数据库中的一行数据对应起来. 我是使用的环境是Spring boot,访问的数据库时MongoDB 实体类遍历 ...

  8. java中遍历实体类,获取属性名和属性值

    方式一(实体类): //java中遍历实体类,获取属性名和属性值 public static void testReflect(Object model) throws Exception{ for ...

  9. Java中的BigDecimal类精度问题

    bigdecimal 能保证精度的原理是:BigDecimal的解决方案就是,不使用二进制,而是使用十进制(BigInteger)+小数点位置(scale)来表示小数,就是把所有的小数变成整数,记录小 ...

随机推荐

  1. 20170809-从URL输入到页面展现

    从URL输入到页面展现 1.输入URL URL:统一资源定位符,是对可以从互联网上得到的资源的位置和访问方法的一种简洁的表示. URL包含以下几部分:协议.服务器名称(或IP地址).路径.参数和查询. ...

  2. CSS实现响应式布局

    用CSS实现响应式布局 响应式布局感觉很高大上,很难,但实际上只用CSS也能实现响应式布局要用的就是CSS中的没接查询,下面就介绍一下怎么运用: 使用@media 的三种方法 1.直接在CSS文件中使 ...

  3. XML学习笔记--背诵版

    前言 一直想系统性的学XML,就没时间学,今晚抽出几个小时时间学完了XML.过几天再过来看看,背一背应该就差不多,记得东西较多,没什么难理解的. XML数据传输格式 第一章 XML概述 1.1 引入 ...

  4. Docker部署LAMP项目

    前言 之前我们学习了如何在Linux部署LAMP项目,今天我们来学习一下如何在Docker下部署LAMP项项目吧! Docker 要求 CentOS 系统的内核版本高于 3.10 ,查看本页面的前提条 ...

  5. GPS同步时钟装置应用及选择

    GPS同步时钟装置应用及选择 GPS是全球定位系统的简称,GPS具有全天时.全天候.高精度.定位和授时服务,GPS卫星授时成本低.安全可靠.覆盖范围广.GPS同步时钟装置,是指从GPS卫星上获取时间信 ...

  6. 迄今为止最硬核的「Java8时间系统」设计原理与使用方法

    为了使本篇文章更容易让读者读懂,我特意写了上一篇<任何人都需要知道的「世界时间系统」构成原理,尤其开发人员>的科普文章.本文才是重点,绝对要读,走起! Java平台时间系统的设计方案 几乎 ...

  7. C++ for循环练习

    #include <stdio.h> //题目:现有公鸡5元一只,母鸡3元一只,小鸡1元3只(小鸡不能单买 最少3只起卖),此时顾客有100元,请问有多少种购买方案. int main() ...

  8. Spring Ioc 依赖查找

    Spring ioc 有依赖查找和依赖注入,之前不太明白依赖查找是什么意思,翻了一大堆博客看了好多定义也不太清楚 ,后来看了小马哥视频,他通过代码演示,清楚地讲解了什么是 依赖查找以及几种依赖查找的方 ...

  9. ASP.NET WebApi实现Token验证

    记录笔记,在博客园中有很多实现Token的方法,这是我看过他们学到的,然后找到适合自己的解决方案,自己无聊总结一下学习经验写下的 WebApi后端接口实现Token验证 Token是在客户端频繁向服务 ...

  10. Natas2 Writeup(水平越权)

    Natas2: 查看源码,发现页面隐藏了一个图片的连接,分析图片,无隐写内容,联想到目录权限问题,访问同级目录http://natas2.natas.labs.overthewire.org/file ...