为什么要用枚举

在博客系统中,一篇文章有且可能有这几种状态, 数据库中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. OOP面向对象三大特点

    OOP面向对象三大特点 (一)封装:将现实中一个事物的属性和功能集中定义在一个对象中.(创建对象) 创建对象的3种方式: 1.直接量方式:(创建一个单独的对象) var obj={ 属性名:值,    ...

  2. 如何让 Git 忽略掉文件中的特定行内容?

    近期在git遇到几个问题,让我重新认识到git的强大性,下面列出来记录一下 有一个数据库的配置文件,在用 git add 添加到 index file 时不能透露了相关配置.而如果用 .gitigno ...

  3. ABP文档 - Quartz 集成

    文档目录 本节内容: 简介 安装 创建工作 调度工作 更多 简介 Quartz 是一个功能完整的开源工作调度系统,可用于最小的应用到一个大型的企业系统.Abp.Quartz 包简单地把Quartz集成 ...

  4. Codeforce 水题报告(2)

    又水了一发Codeforce ,这次继续发发题解顺便给自己PKUSC攒攒人品吧 CodeForces 438C:The Child and Polygon: 描述:给出一个多边形,求三角剖分的方案数( ...

  5. Android Monkey压力测试介绍

    monkey:通过Monkey程序模拟用户触摸屏幕.滑动Trackball. 按键等操作来对设备上的程序进行压力测试,检测程序多久的时间会发生异常. Monkey的构架 Monkey的参数 Monke ...

  6. [故障公告] 13:52-14:03,访问量突增,博客web服务器CPU 100%

    13:52-14:03,由于访问量突增,博客web服务器全线CPU 100%,造成博客站点不正常访问,由此给您带来麻烦,请您谅解. 为了迎接访问量的增长给web服务器CPU带来的巨大压力,上周我们已经 ...

  7. SpringMVC4+MyBatis3+SQLServer 2014 整合(包括增删改查分页)

    前言 说起整合自然离开ssm,我本身并不太喜欢ORM,尤其是MyBatis,把SQL语句写在xml里,尤其是大SQL,可读性不高,出错也不容易排查. 开发环境 idea2016.SpringMVC4. ...

  8. PDO(PHP Data Object)数据访问抽象层

    1.可以访问其它数据库2.具有事务功能3.带有预处理语句功能(防止SQL注入攻击) 访问数据库 PDO::__construct ( string $dsn [, string $username [ ...

  9. 调用android方法,出现版本太低解决方法

    原因如下图所示: 调用需要API级别11,当前是8. 解决方法如下图所示: 点击

  10. session 与 cookie的区别用法

    //设置cookie方法 setcookie("name",'zhangsan'); setcookie("name",'zhangsan',time()+60 ...