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. .Net Core 为 x86 和 x64 程序集编写 AnyCPU 包装

    前言 这几天研究了一下 vJoy 这个虚拟游戏手柄驱动,感觉挺好玩的.但是使用时发现一个问题,C# SDK 中的程序集被分为 x86 和 x64 两个版本,如果直接在 AnyCPU 平台编译运行就有隐 ...

  2. java CRC16 算法

    代码摘自:https://www.cnblogs.com/lujiannt/p/9246256.html 1.CRC16算法 public class CRC16Util { /** * 计算CRC1 ...

  3. 分享一个快速审查js操作Dom的css

    第一步 打开开发者工具第二步 打开 Sources 面板第三步 执行用户操作让对象可见(例如鼠标悬停)第四步 在元素可见的时候按下 F8(与“暂停脚本执行”按钮相同)第五步 点击开发者工具左上角的“选 ...

  4. iview必备技能一、表单验证规则

    iView表单组件使用async-validator验证器对表单域中数据进行验证,给Form 设置属性 rules,同时给需要验证的 FormItem 设置属性 prop 指向对应字段即可. 完整的验 ...

  5. js的变量——基本类型保存在栈中,引用类型保存在堆中

    javascript的基本类型:Undefined,Null,Boolean,Number,String 引用类型:Object,Array,Function 基本类型值在内存中占据固定大小,被保存在 ...

  6. GPS授时器简介

    GPS授时器简介 GPS是全球定位系统的简称.GPS定位卫星在全球范围内进行定位.导航的系统.GPS所具有的全天候.高精度和自动测量的特点,已经融入到国民经济建设.国防建设和社会发展的各个领域.而在授 ...

  7. 如何在Flutter中使用flutter_markdown

    很多博客,论坛都支持markdown语法,flutter也有支持markdown语法的插件flutter_markdown 安装依赖 dependencies: flutter: sdk: flutt ...

  8. Core + Vue 后台管理基础框架3——后端授权

    1.前言 但凡业务系统,授权是绕不开的一环.见过太多只在前端做菜单及按钮显隐控制,但后端裸奔的,觉着前端看不到,系统就安全,掩耳盗铃也好,自欺欺人也罢,这里不做评论.在.NET CORE中,也见过不少 ...

  9. LeetCode专题——详解搜索算法中的搜索策略和剪枝

    本文始发于个人公众号:TechFlow,原创不易,求个关注 今天是LeetCode专题第20篇文章,今天讨论的是数字组合问题. 描述 给定一个int类型的候选集,和一个int类型的target,要求返 ...

  10. js获得用户网络状况API

    js获得用户网络状况API 这是一个实验中的功能,目前还有许多浏览器不兼容此功能某些浏览器尚在开发中 1. 网络类型 effectiveType: 可以得到2g,3g,4g connectionInf ...