为什么要用枚举

在博客系统中,一篇文章有且可能有这几种状态, 数据库中article文章表中state字段存储数值,表示其状态:

  • 0(已发表Published)
  • 1(草稿Draft)
  • 2(撤回撤回(Delete)

文章实体类中用整数类型的state实例变量标识状态:

public class Article {
/* 文章状态可能值:0,1,2 */
private int state;
...
}

Service层调用DAO层修改文章状态为‘已发表’:

/**
* dao接口修改文章状态方法
* @param articleId 文章ID
* @param state 状态
*/
int updateState(int articleId, int state); // `Service`层修改文章状态的调用Dao代码:
articleDao.updateState(id, 0);

以上代码有两个问题:

  1. state参数传递并没有限定范围(0,1,2);
  2. 传递数据参数的代码,缺少语义,不看文档或注释不知道0是什么含义;

先来解决第二个问题, 在JDK1.5前常用的解决方式:

/**
* 定义了文章的状态
*/
public interface ArticleState{
// 发布状态
int PUBLISHED = 0;
// 草稿状态
int DRAFT = 1;
// 撤回状态
int DELETE = 2;
}

此时修改文章状态的代码:

articleDao.updateState(id, ArticleState.PUBLISHED);

然而此处没有限制必须通过ArticleState传递参数,JDK1.5后提供了枚举来解决这类问题。

Java中声明

在java中,使用enum关键字声明枚举类

/**
* 文章状态枚举类
*/
public enum ArticleStateEnum{
PUBLISHED,
DRAFT,
DELETE;
}

然后修改DAO接口:

/**
* dao接口修改文章状态方法
* @param articleId 文章ID
* @param state 状态
*/
int updateState(int id, ArticleStateEnum state);

接着Service调用:

// 修改文章状态为发表
articleDao.updateState(id, ArticleStateEnum.PUBLISHED);

以上代码语义清晰,现在传递参数的类型为ArticleStateEnum, 解决了之前描述的两个问题

枚举的本质

使用JDK附带工具javap反编译生枚举类字节码, 注javap反编译只会得到public成员:

看反编译的得到的代码:

  1. class声明,意味着枚举的本质也是类;
  2. 父类声明为java.lang.Enum<>, 意味着枚举类不允许显式使用extends声明父类,包括声明为java.lang.Enum<>也会报错;
  3. 枚举常量,通过public static final修饰符实现,ArticlestateEnum类型声明,意味着所有枚举常量本质是当前枚举类的对象;
  4. values()方法valueOf(String)方法;

这些转换工作是javac编译器帮我们实现的,JVM并不知道枚举的存在,javac帮我们做了一些语法上的转化、简化程序员编程,这种方式称为语法糖。

枚举类VS普通类

枚举类就是类,按照这个逻辑来测试下它和普通类的差别

添加构造函数:

红色行提示编译错误“找不到这样的构造函数”,常量声明处添加参数,如下代码正确:

public enum ArticleStateEnum{
PUBLISHED(0, "已发布"),
DRAFT(1, "草稿"),
DELETE(2, "撤销"); /** 代表的数值 */
private int value;
/** 信息提示 */
private String message; ArticleStateEnum(int value, String message) {
this.value = value;
this.message = message;
} // get方法
}

可以推测到常量声明的地方,等价于调用构造函数,通常我们都会为字段添加GET方法不添加SET方法,保证枚举常量的不变性。

枚举类VS普通类的不同点:

  1. 不可以显示声明继承关系;
  2. 常量声明,等价调用构造方法;
  3. 允许有多个构造方法,但修饰符有且仅是private;
  4. 其他地方同类一般无二,可以添加自定添加(方法、字段,抽象成员), 实现接口;

枚举类VS匿名类

看看以下如此夸张的写法,也能编译成功:

观察生成的字节码文件:

把枚举常量声明的地方替换成构造方法调用new ArticleStateEnum(v1, m1),这不就是匿名类的声明吗!

现在向枚举类内添加抽象方放,看看结果:

编译报错“提示有抽象方法未实现”,验证了前面的猜想,这是匿名类的实现,不过不可以显式的使用使用匿名实现枚举类的方式!

常用方法

详细参见API文档Enum类:

public enum ArticleStateEnum{
PUBLISHED,
DRAFT,
DELETE; public static void main(String[] args) {
ArticleStateEnum[] states = ArticleStateEnum.values(); // 1. 获得所有枚举常量
for(ArticleStateEnum state: states) {
System.out.println("序号:" + state.ordinal() + " 名字:" + state); //2. 输出声明序号和名称
} System.out.println("......................................");
ArticleStateEnum draft = ArticleStateEnum.valueOf("DRAFT"); //3. 获得某个枚举常量,依据字符串
if(ArticleStateEnum.DRAFT == draft) {
System.out.println(ArticleStateEnum.valueOf("DRAFT").name()); //4. name方法输出名字
}
}
}

输出...

序号:0 名字:PUBLISHED
序号:1 名字:DRAFT
序号:0 名字:DELETE DRAFT

JAVA中枚举的缺点

java中枚举给我们带来强大的语义的时候,又由于枚举常量对象的本质,给我们带了来庞大性,不如C语言的枚举的轻量:

  1. 枚举常量不可以像C语言一样使用移位运算。
  2. 枚举常量和外部交互麻烦,比如:
    • 在mybatis中保存带有枚举字段的实体时,需要你编写转化器(除非按照默认的声明顺序);
    • 转化为JSON数据时;
    • Spring MVC对请求参数封装时,需要自定义转换器;
  3. 枚举常量无法继承,意味着相似的枚举类之间无法继承,导致产生冗余代码;

建议无特殊情况还是使用枚举常量,毕竟软件的正确性是最重要的

Java 枚举详解的更多相关文章

  1. java枚举详解

    枚举的本质是类,枚举是用来构建常量数据结构的模板(初学者可以以此方式理解: public static final X=xxx),枚举的使用增强了程序的健壮性,在引用一个不存在的枚举值的时候,编译器会 ...

  2. Java Annotation详解 理解和使用Annotation

    系统中用到了java注解: 查了一下如何使用注解,到底注解是什么: (1)创建方法:MsgTrace Java Class==> 在Create New Class中: name:输入MsgTr ...

  3. Java IO 详解

    Java IO 详解 初学java,一直搞不懂java里面的io关系,在网上找了很多大多都是给个结构图草草描述也看的不是很懂.而且没有结合到java7 的最新技术,所以自己来整理一下,有错的话请指正, ...

  4. java关键字(详解)

    目录 1. 基本类型 1) boolean 布尔型 2) byte 字节型 3) char 字符型 4) double 双精度 5) float 浮点 6) int 整型 7) long 长整型 8) ...

  5. Java集合详解3:Iterator,fail-fast机制与比较器

    Java集合详解3:Iterator,fail-fast机制与比较器 今天我们来探索一下LIterator,fail-fast机制与比较器的源码. 具体代码在我的GitHub中可以找到 https:/ ...

  6. Java集合详解3:一文读懂Iterator,fail-fast机制与比较器

    <Java集合详解系列>是我在完成夯实Java基础篇的系列博客后准备开始写的新系列. 这些文章将整理到我在GitHub上的<Java面试指南>仓库,更多精彩内容请到我的仓库里查 ...

  7. Java集合详解1:一文读懂ArrayList,Vector与Stack使用方法和实现原理

    本文非常详尽地介绍了Java中的三个集合类 ArrayList,Vector与Stack <Java集合详解系列>是我在完成夯实Java基础篇的系列博客后准备开始写的新系列. 这些文章将整 ...

  8. 9种Java单例模式详解(推荐)

    单例模式的特点 一个类只允许产生一个实例化对象. 单例类构造方法私有化,不允许外部创建对象. 单例类向外提供静态方法,调用方法返回内部创建的实例化对象.  懒汉式(线程不安全) 其主要表现在单例类在外 ...

  9. Java内部类详解

    Java内部类详解 说起内部类这个词,想必很多人都不陌生,但是又会觉得不熟悉.原因是平时编写代码时可能用到的场景不多,用得最多的是在有事件监听的情况下,并且即使用到也很少去总结内部类的用法.今天我们就 ...

随机推荐

  1. git 命令用法 流程操作

    Git 是一款免费的.开源的.分布式的版本控制系统.旨在快速高效地处理无论规模大小的任何软件工程. 每一个 Git克隆 都是一个完整的文件库,含有全部历史记录和修订追踪能力,不依赖于网络连接或中心服务 ...

  2. 纪中集训 Day 5

    不知不觉已经day 5了啊 今天早上醒来,觉得要AK的节奏,结果就立flag了 - - 30分QAQ 其实第一题应该得想得到的,还有T2也能够解决的(话说后来看别人的代码写的好赞啊QAQ) 然后下午就 ...

  3. 【PHP系列】PHP推荐标准之PSR-1,PSR-2

    说起码代码,刚上大学那会,老师就教导我们,要严格,规范的,把代码写好.代码如人,工工整整.提起规范化的代码,从一开始用命令行编辑C语言代码就开始控制,强制自己按照相关的标准来,所以,现在写代码,不规范 ...

  4. iOS开发-APP测试基本流程

    1. UI 测试app主要核ui与实际设计的效果图是否一致:交互方面的问题建议,可以先与产品经理确认,确认通过后,才开始让开发实施更改或优化 2. 功能测试根据软件说明或用户需求验证App的各个功能实 ...

  5. C# 知识回顾 - 装箱与拆箱

    装箱与拆箱 目录 生活中的装箱与拆箱 C# 的装箱与拆箱 值类型和引用类型 装箱 拆箱 生活中的装箱与拆箱    我们习惯了在网上购物,这次你想买本编程书 -- <C 语言从入门到放弃> ...

  6. everything 快速搜索有代价

    我在一台电脑上运行过everything后,把它拷贝到另一台电脑上运行,前一台电脑上的搜索结果记录居然还,包括文件类型,标题,大小,位置.天呐··· 虽然看不到内容,但对于一个社工来说,一个标题完全足 ...

  7. 第十篇 一个利用反射实现的Excel导出

    前些天写了个导出Excel的公共类,因为项目中也需要上传Excel,没有利用Office组件,(PS:Office在上传文件时候,Excel进程无法关闭,会导致上传和打开失败)有的说利用Kill把进程 ...

  8. Javascript中好用更改时间的方法

    <script type="text/javascript"> //格式化时间格式的字符串 String.prototype.myTimes = function () ...

  9. http-server 命令行

    安装 (全局安装加 -g) : npm install http-server (npm install --global http-server) 在站点目录下开启命令行输入 http server ...

  10. JS入门(一)

    在学js之前,我们应该先清楚js是什么,js全称JavaScript.是一门基于对象和事件的,有安全性的脚本语言.所谓脚本语言,就是一行一行执行的,就像剧本一样,一句句的往下读.而对象和事件,则是js ...