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. 网络编程概念 和OSI七层结构简介

    什么是网络编程 网络通常指的是计算机中的互联网,是由多台计算机通过网线或其他媒介相互链接组成的 编写基于网络的应用程序的过程序称之为网络编程 学习网络编程就是要学习利用网络来与另一台计算机相互传输数据 ...

  2. Feign 客户端的使用 二

    一.Feign的使用(客户端调用 json/xml格式的接口) 1.服务端接口编写 <parent> <groupId>org.springframework.boot< ...

  3. vs code开发python时找不到当前目录下的文件、UnicodeDecodeError: 'gbk'

    一.vs code开发python时找不到当前目录下的文件, file = open("readme.txt")一直报错,找不到目录下面的文件 原来vscode 默认都是以打开的项 ...

  4. 2653 区间xor

    前言 这个题目在我之前那篇c++位运算的的随笔中提到过. 有兴趣的话去看看吧! 飞机场:https://www.cnblogs.com/laoguantongxiegogofs/p/12444517. ...

  5. PHP的for循环

    For循环执行代码块指定的次数,或者当指定的条件为真时循环执行代码块.for循环用于您预先知道脚本需要运行的次数的情况 for(初始值:条件:增量) { 要执行的代码: } <html> ...

  6. [android]R.class里有ID,onCreate方法里调用findViewById返回空

    在做android练习,一个新手错误,记录一下: 在练习android权威编程指南时,第5章 第二个Activity部分练习,出现标题问题,代码还原如下: protected void onCreat ...

  7. 序列化器:ModelSerializer

    ModelSerializer 类提供了一个快捷方式,可让你基于 Models 自动创建一个 Serializer 类,其中的字段与模型类字段对应. ModelSerializer 类与常规 Seri ...

  8. 三、create-react-app新旧版中使用less和antd并修改主题颜色

    引入less 如果项目根目录中没有config文件夹,首先暴露出项目配置文件,项目下执行: npm run eject 如果项目是从git仓库中pull下来的的话,必须确保本地项目与仓库中没有冲突,才 ...

  9. 有关EPX Studio使用DELPHI5作为基础环境版本的说明

    英巴卡迪诺北京科技有限公司,地址是北京市朝阳门外大街18号丰联广场B座813B,这家公司这家公司不拥有:delphi 1.0~delphi7.0 .delphi 2005版本的著作权,这些都还是属于B ...

  10. ArrayList,HashSet,SortedSet之间的区别是什么?

    今天看Redis官方案例,出现了列表和集合概念,列表在Java中指的就是List,集合在Java中指的就是Set,那么怎么实现列表和集合,以及它们有什么区别呢? 我写了个Demo演示下: import ...