为什么要用枚举

在博客系统中,一篇文章有且可能有这几种状态, 数据库中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. oozie配置安装与原理

     概述 当前开源的hadoop任务工作流管理主要有oozie和Azkaban,本文先介绍oozie的配置安装与基本运行原理. 配置安装 (参考https://segmentfault.com/a/11 ...

  2. V3学院XILINX FPGA寒假班培训感受 江苏大学 电子信息科学与技术 邓普建

    事先申明一点,我是大一的学生,因此会站在一个新生的角度叙述. 刚开始接触V3学院是在江苏大学与V3学院合办的FPGA/SOC培训中,那是对全校开放的免费培训,历时三个周末.我那时有幸从头听到了尾,觉得 ...

  3. 非名校毕业年薪20W程序员 心得分享

    大家好,我是落落http://blog.csdn.net/robinson1988/ QQ692162374,其实我选择Oracle是一件很神奇的事情,但是现在回想起来又觉得是命中注定.我2006年就 ...

  4. jQuery DataTable 删除数据后重新加载

    问题描述: 利用jQuery Datatable和artTemplate组合来做的表格.但是当删除数据时,需要重新加载table里的数据.但是问题是datatable并没有直接的重新渲染,反而给数据累 ...

  5. 剑指offer编程题Java实现——面试题11数值的整数次方

    题目: 实现函数double power(double base,int exponent),求base的exponent次方.不得使用库函数,同时不需要考虑大数问题. 解题思路:最一般的方法实现数值 ...

  6. Spring+SpringMVC+MyBatis+easyUI整合基础篇(五)讲一下maven

    github地址,点这里. 项目效展示,点这里.账号:admin 密码:123456 下一篇文章开始,所有的项目源码都是与maven整合后的代码了,所以这一篇讲一讲maven. 1.简单介绍 我们看一 ...

  7. 从C#到TypeScript - Proxy

    总目录 从C#到TypeScript - 类型 从C#到TypeScript - 高级类型 从C#到TypeScript - 变量 从C#到TypeScript - 接口 从C#到TypeScript ...

  8. javascript中加var和不加var的区别

    Javascript是遵循ECMAScript标准下的一个产物,自然ECMAScript的标准其要遵循. 先来看下var关键字的定义和用法 var 语句用于声明变量. JavaScript 变量的创建 ...

  9. SQL SERVER 事务日志 解析

    1 基本介绍 每个数据库都具有事务日志,用于记录所有事物以及每个事物对数据库所作的操作. 日志的记录形式需要根据数据库的恢复模式来确定,数据库恢复模式有三种: 完整模式,完全记录事物日志,需要定期进行 ...

  10. nginx配置之深入理解

    继上一篇<debian+nginx配置初探--php环境.反向代理和负载均衡>成功之后,有点小兴奋,终于不用整lvs那么复杂来搞定负载,但还是有很多概念没弄清楚. 什么是CGI.FastC ...