枚举类型是 Java 5 中新增特性的一部分, 它是一种特殊的数据类型, 之所以特殊是因为它既是一种类 (class) 类型却又比类类型多了些特殊的约束, 但是这些约束的存在也造就了枚举类型的简洁性, 安全性以及便捷性.
△有的地方还没有学的透彻, 之后会继续学习修改更新本文章
1. 枚举类学习
1.1 定义枚举类
枚举类可以实现一个或多个接口, 使用 enum 定义的枚举类默认继承了 java.lang.Enum 类, 而不是默认继承 Object 类, 因此枚举类不能显示继承其他父类. 其中 java.lang.Enum 类实现了 java.lang.Serializable 和 java.lang.Comparable 两个接口. 使用 enum 定义, 非抽象的枚举类默认会使用 final 修饰, 因此枚举类不能派生子类.
枚举类的构造器只能使用 private 访问控制符, 如果省略了构造器的访问控制符, 则默认使用 private 修饰; 如果强制指定访问控制符, 则只能指定 private 修饰符.
枚举类的所有实例必须在枚举类的第一行显式列出, 否则这个枚举类永远都不能产生实例. 列出这些实例时, 系统会自动添加 public static final 修饰, 无须程序员显式添加.
枚举类默认提供了一个 values()方法, 该方法可以很方便地遍历所有的枚举值.
如下定义周一到周日的常量
- //Day.class
- // 枚举类型, 使用关键字 enum
- enum Day {
- MONDAY, TUESDAY, WEDNESDAY,
- THURSDAY, FRIDAY, SATURDAY, SUNDAY
- }
相当简洁, 在定义枚举类型时我们使用的关键字是 enum, 与 class 关键字类似, 只不过前者是定义枚举类型, 后者是定义类类型.
1.2 枚举类的实现原理
我们大概了解了枚举类型的定义与简单使用后, 现在有必要来了解一下枚举类型的基本实现原理. 实际上在使用关键字 enum 创建枚举类型并编译后, 编译器会为我们生成一个相关的类, 这个类继承了 Java API 中的 java.lang.Enum 类, 也就是说通过关键字 enum 创建枚举类型在编译后事实上也是一个类类型而且该类继承自 java.lang.Enum 类.
查看反编译 Day.class 文件:
- // 反编译 Day.class
- final class Day extends Enum
- {
- // 编译器为我们添加的静态的 values()方法
- public static Day[] values()
- {
- return (Day[])$VALUES.clone();
- }
- // 编译器为我们添加的静态的 valueOf()方法, 注意间接调用了 Enum 也类的 valueOf 方法
- public static Day valueOf(String s)
- {
- return (Day)Enum.valueOf(com/zejian/enumdemo/Day, s);
- }
- // 私有构造函数
- private Day(String s, int i)
- {
- super(s, i);
- }
- // 前面定义的 7 种枚举实例
- public static final Day MONDAY;
- public static final Day TUESDAY;
- public static final Day WEDNESDAY;
- public static final Day THURSDAY;
- public static final Day FRIDAY;
- public static final Day SATURDAY;
- public static final Day SUNDAY;
- private static final Day $VALUES[];
- static
- {
- // 实例化枚举实例
- MONDAY = new Day("MONDAY", 0);
- TUESDAY = new Day("TUESDAY", 1);
- WEDNESDAY = new Day("WEDNESDAY", 2);
- THURSDAY = new Day("THURSDAY", 3);
- FRIDAY = new Day("FRIDAY", 4);
- SATURDAY = new Day("SATURDAY", 5);
- SUNDAY = new Day("SUNDAY", 6);
- $VALUES = (new Day[] {
- MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY, SUNDAY
- });
- }
- }
1从反编译的代码可以看出编译器确实帮助我们生成了一个 Day 类 (注意该类是 final 类型的, 将无法被继承) 而且该类继承自 java.lang.Enum 类, 该类是一个抽象类(稍后我们会分析该类中的主要方法).
2除此之外, 编译器还帮助我们生成了 7 个 Day 类型的实例对象分别对应枚举中定义的 7 个日期, 这也充分说明了我们前面使用关键字 enum 定义的 Day 类型中的每种日期枚举常量也是实实在在的 Day 实例对象, 只不过代表的内容不一样而已. 注意编译器还为我们生成了两个静态方法, 分别是
values()和 valueOf()
.
3到此我们也就明白了, 使用关键字 enum 定义的枚举类型, 在编译期后, 也将转换成为一个实实在在的类, 而在该类中, 会存在每个在枚举类型中定义好变量的对应实例对象, 如上述的 MONDAY 枚举类型对应
public static final Day MONDAY;
, 同时编译器会为该类创建两个方法, 分别是
values()和 valueOf()
. 到此相信我们对枚举的实现原理也比较清晰. 下面我们深入了解一下 java.lang.Enum 类以及 values()和 valueOf()的用途.
1.3 枚举的常见方法
2. 枚举类使用
2.1 常量
系统里实现常量的三种方式接口常量, 类常量, 枚举常量
2.1.1 接口常量
写法 (1) 利弊: 用到 DefaultValues.DEFAULT_AP 的含义, 必须看类里的注释, 知道他表示中心. 如果常量很多的话, 把所有的常量都放在这一个接口里边, 这种方式感觉也不是很友好.
- /**
- * 系统默认值
- *
- */
- public class DefaultValues {
- /**
- * 默认密码
- */
- public static final String DEFAULT_PASSWORD = "000000";
- /**
- * 默认用户类型
- */
- public static final String DEFAULT_USER_TYPE = UserType.NormalUser.value();
- /**
- * 默认获取 API 名称
- */
- public static final String DEFAULT_API = "api";
- /**
- * 默认系统字符编码
- */
- public static final String DEFAULT_ENCODING = "UTF-8";
- /** 集群规模 */
- public static final long CLUSTER_SIZE = 1000;
- }
写法 (2) 利弊: 公司的接口常量是在接口里定义静态内部类, 他可以把不同的功能的常量类进一步分类. 把不同功能的常量放在了接口的内部类里, 通过不同的内部类可以清楚的知道一个常量的含义.
- public class Constants {
- public static class MimeType{
- public static final String BIN = "application/octet-stream";
- public static final String CSS = "text/css";
- public static final String DOC = "application/msword";
- public static final String DOCX = "";
- public static final String EXE = "application/octet-stream";
- public static final String GTAR = "application/x-gtar";
- public static final String GZ = "application/x-gzip";
- public static final String HTM = "text/html;charset=utf-8";
- public static final String ICO = "image/x-icon";
- public static final String JPEG = "image/jpeg";
- public static final String JPG = "image/jpeg";
- public static final String JS = "application/x-javascript;charset=utf-8";
- public static final String JSON = "application/json;charset=utf-8";
- public static final String FORM = "application/x-www-form-urlencoded; charset=UTF-8";
- public static final String MULTIPART = "multipart/form-data; charset=UTF-8";
- public static final String MHT = "message/rfc822";
- public static final String MHTML = "message/rfc822";
- public static final String MOV = "video/quicktime";
- public static final String MP3 = "audio/mpeg";
- public static final String MPE = "video/mpeg";
- public static final String MPEG = "video/mpeg";
- public static final String MPG = "video/mpeg";
- public static final String PDF = "application/pdf";
- public static final String PPT = "application/vnd.ms-powerpoint";
- public static final String RTF = "application/rtf";
- public static final String SWF = "application/x-shockwave-flash";
- public static final String TAR = "application/x-tar";
- public static final String TXT = "text/plain;charset=utf-8";
- public static final String WAV = "audio/x-wav";
- public static final String xml = "text/xml;charset=utf-8";
- public static final String ZIP = "application/zip";
- }
- public static class DataState{
- public static final String FLAG_REMOVE = "Y";
- public static final String FLAG_NORMAL = "N";
- }
- /**
- * 应用服务器实例运行状态
- */
- public static class ServerASInstanceState{
- public static final int RUNNING = 1;
- public static final int SHUT_OFF = 2;
- }
- /**
- * WebServices 接口分析
- */
- public static class WebServicesType{
- /** 先接收数据, 在返回接口情况的接口 **/
- public static final String IN_OUT = "IO";
- /** 先发数据请求, 后返回数据的接口 **/
- public static final String OUT_IN = "OI";
- /** 只发送数据的接口 **/
- public static final String OUT= "O";
- /** 只接收数据的接口 **/
- public static final String IN = "I";
- }
- /**
- * 任务调度使用
- */
- public static class TaskScheduling{
- /** 任务 ID **/
- public static final String TASK_ID = "taskID";
- /** 任务 URL **/
- public static final String TASK_URI = "taskURI";
- /** 任务 URL **/
- public static final String TASK_NAME = "taskName";
- /** 任务目标服务器 IP **/
- public static final String TASK_SERVER_IP = "taskServerIp";
- /** 任务目标服务器 IP **/
- public static final String TASK_SERVER_PORT = "taskServerPort";
- /** 任务状态启用 **/
- public static final int TASK_ENABLED = 1;
- /** 任务状态禁用 **/
- public static final int TASK_DISABLE = 0;
- /** 每年任务 **/
- public static final int TYPE_EVERY_YEAR= 1;
- /** 每月任务 **/
- public static final int TYPE_EVERY_MONTH = 2;
- /** 每日任务 **/
- public static final int TYPE_EVERY_DAY = 3;
- /** 每周任务 **/
- public static final int TYPE_EVERY_WEEK = 4;
- /** 单次任务 **/
- public static final int TYPE_SINGLE = 5;
- }
- }
2.1.2 类常量
虽然有了枚举, 可能是由于设计者习惯问题, 还有很多人用的类常量, 定义了类常量, 用一个 Map<Integer, String > 来封装常量对应的信息, 在 static 代码块里, 类初始化的时候执行一次 put. 用的时候 ResponseCode.RESP_INFO.get("DATABASE_EXCEPTION"); 就能取出响应信息 由于项目是前后端分离, 在接口文档里需要写上状态码, 还得写上状态码对应的提示信息, 而且我们的响应类 RespInfo 有 message 属性, 就是保存常量类里状态码对应的信息的.
- public class ResponseCode {
- /** 系统处理正常 */
- public static final int SUCCESS_HEAD = 0;
- /** 系统处理未知异常 */
- public static final int EXCEPTION_HEAD = 1;
- /** JSON 解析错误 */
- public static final int JSON_RESOLVE = 2;
- /** 类型不匹配 */
- public static final int TRANSTYPE_NO = 3;
- /** Head - messageID 未赋值 */
- public static final int HEAD_messageID = 4;
- /** Head - timeStamp 未赋值 */
- public static final int HEAD_timeStamp = 5;
- /** Head - messengerID 未赋值 */
- public static final int HEAD_messengerID = 6;
- /** Head - transactionType 未赋值 */
- public static final int HEAD_transactionType = 7;
- /** digest 校验不通过 */
- public static final int HEAD_DIGEST = 8;
- /** src 校验不通过 */
- public static final int HEAD_SRC_NULL = 10;
- /** 协议包含非法字符 */
- public static final int ILLEGAL_MESSAGE = 11;
- /** 数据库异常 */
- public static final int DATABASE_EXCEPTION = 9;
- public static final Map<Integer, String> RESP_INFO = new HashMap<Integer, String>();
- static {
- // Head 相关
- RESP_INFO.put(SUCCESS_HEAD, "系统处理正常");
- RESP_INFO.put(EXCEPTION_HEAD, "系统处理未知异常");
- RESP_INFO.put(JSON_RESOLVE, "JSON 解析错误");
- RESP_INFO.put(TRANSTYPE_NO, "类型不匹配");
- RESP_INFO.put(HEAD_messageID, "messageID 未赋值");
- RESP_INFO.put(HEAD_timeStamp, "timeStamp 未赋值");
- RESP_INFO.put(HEAD_messengerID, "messengerID 未赋值");
- RESP_INFO.put(HEAD_transactionType, "transactionType 未赋值");
- RESP_INFO.put(HEAD_DIGEST, "digest 校验不通过");
- RESP_INFO.put(DATABASE_EXCEPTION, "数据库异常");
- RESP_INFO.put(HEAD_SRC_NULL, "src 未赋值");
- RESP_INFO.put(ILLEGAL_MESSAGE, "协议包含非法字符");
- }
- }
2.1.3 枚举常量
所有的枚举类都是 Enum 类的子类, 就行 Object 类一样, 只是没有写出来, 所以可以枚举类可调用 Enum 的方法. 注意是逗号分隔属性, 只有属性后边没有方法的话, 最后加不加分号都行. 写法(1)
- public enum StateType {
- /**
- * 成功返回状态
- */
- OK(200,"OK"),
- /**
- * 请求格式错误
- */
- BAD_REQUEST(400,"bad request"),
- /**
- * 未授权
- */
- UNAUTHORIZED(401,"unauthorized"),
- /**
- * 没有权限
- */
- FORBIDDEN(403,"forbidden"),
- /**
- * 请求的资源不存在
- */
- NOT_FOUND(404,"not found"),
- /**
- * 该 http 方法不被允许
- */
- NOT_ALLOWED(405,"method not allowed"),
- /**
- * 请求处理发送异常
- */
- PROCESSING_EXCEPTION(406,"Handling Exceptions"),
- /**
- *
- * 请求处理未完成
- */
- PROCESSING_UNFINISHED(407,"To deal with unfinished"),
- /**
- * 登录过期
- */
- BEOVERDUE(408,"Be overdue"),
- /**
- * 用户未登录
- */
- NOT_LOGIN(409,"Not logged in"),
- /**
- * 这个 url 对应的资源现在不可用
- */
- GONE(410,"gone"),
- /**
- * 请求类型错误
- */
- UNSUPPORTED_MEDIA_TYPE(415,"unsupported media type"),
- /**
- * 校验错误时用
- */
- UNPROCESSABLE_ENTITY(422,"unprocessable entity"),
- /**
- * 请求过多
- */
- TOO_MANY_REQUEST(429,"too many request");
- private int code;
- private String value = null;
- private StateType(int code,String value) {
- this.code = code;
- this.value = value;
- }
- public String value() {
- return this.value;
- }
- public int getCode() {
- return code;
- }
- public static Boolean isValidateStateType(String... stateType) {
- for (int i = 0; i < stateType.length; i++) {
- StateType [] value = StateType.values();
- boolean falg = false;
- for(StateType type : value) {
- if(type.value.equals(stateType[i])) {
- falg = true;
- }
- }
- if(!falg) {
- return falg;
- }
- }
- return true;
- }
- }
- /* 使用 */
- public static void main(String[] args) {
- System.out.println("状态码:"+StateType.getCode());
- System.out.println("错误信息:"+StateType.getValue());
- }
写法(2)
- public enum Level {
- /**
- * 第一层
- */
- One(1),
- /**
- * 第二层
- */
- Two(2),
- /**
- * 第三层
- */
- Three(3),
- /**
- * 第四层
- */
- Four(4),
- /**
- * 第五层
- */
- Five(5);
- private int value;
- Level(int value) {
- this.value = value;
- }
- public int value() {
- return this.value;
- }
- public static Boolean isValidateLevel(int level) {
- Level [] value = Level.values();
- boolean falg = false;
- for (Level pl : value){
- if(pl.value == level){
- falg = true;
- }
- }
- return falg;
- }
- }
- /* 使用 */
- public static void main(String[] args) {
- System.out.println("楼层:"+Level.Three);
- }
2.2 switch 结合枚举类
JDK1.6 之前的 switch 语句只支持 int,char,enum 类型, 使用枚举, 能让我们的代码可读性更强.
枚举是声明一组命名的常数, 当一个变量有几种可能的取值时, 可以将它定义为枚举类型. 枚举是将变量的值一一列出来, 变量的值只局限于列举出来的值的范围内.
△注意: 枚举只是枚举类型, 不能够赋值操作. 如下: GREEN 默认值为 0, 但是 GREEN 不能 = 0, 因为数据类型不一样. 枚举中变量未直接赋值, 默认等于前一个变量值加一, 起始值默认为 0.
- enum Signal {
- GREEN, YELLOW, RED
- }
- public class TrafficLight {
- Signal color = Signal.RED;
- public void change() {
- switch (color) {
- case RED:
- color = Signal.GREEN;
- break;
- case YELLOW:
- color = Signal.RED;
- break;
- case GREEN:
- color = Signal.YELLOW;
- break;
- }
- }
- }
2.3 向枚举中添加新的方法
如果打算自定义自己的方法, 那么必须在 enum 实例序列的最后添加一个分号(";"),java 中要求必须先定义 java 实例.
- public enum ChannelEnum {
- MSG_CENTER_CHANNEL1("msg_center_channel1"),
- MSG_CENTER_CHANNEL("msg_center_channel");
- private String channel = null;
- private ChannelEnum(String channel) {
- this.channel = channel;
- }
- public String getChannel() {
- return this.channel;
- }
- }
2.4 实现接口
所有的枚举都继承自 java.lang.Enum 类. 由于 Java 不支持多继承, 所以枚举对象不能再继承其他类.
如果由枚举类来实现接口里的方法, 则每个枚举值在调用该方法时都有相同的行为方式(因为方法体完全一样). 如果需要每个枚举值在调用该方法时呈现出不同的行为方式, 则可以让每个枚举值分别来实现该方法, 每个枚举值提供不同的实现方式, 从而让不同的枚举值调用该方法时具有不同的行为方式.
- public interface Behaviour {
- void print();
- String getInfo();
- }
- public enum Color implements Behaviour{
- 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 getInfo() {
- return this.name;
- }
- // 接口方法
- @Override
- public void print() {
- System.out.println(this.index+":"+this.name);
- }
- }
2.5 使用接口组织枚举
- public interface Food {
- enum Coffee implements Food{
- BLACK_COFFEE,DECAF_COFFEE,LATTE,CAPPUCCINO
- }
- enum Dessert implements Food{
- FRUIT, CAKE, GELATO
- }
- }
- https://www.cnblogs.com/lihaoyang/p/6913295.html
- https://segmentfault.com/a/1190000007688908
来源: https://juejin.im/post/5c13133be51d456fac740c98