Java枚举深度解读,看这篇就够了
ztj100 2024-12-05 18:08 12 浏览 0 评论
作者 | 涛GuoGuo的跟屁虫丶博Ke
来源 | urlify.cn/aaamQf
66套java从入门到精通实战课程分享
Java枚举
1、枚举类概念的理解与定义
- 一个类的对象是有限个,确定的,我们称此为枚举类。
- 当需要定义和维护一组常量时,强烈建议使用枚举类。
- 如果一个枚举类中只有一个对象,则可以作为单例模式的实现方式。
通俗的说:一个类被设计为包含固定实例数量的特殊类,我们给他的定义是枚举类。
注意:
1.枚举类不能被 new 出来,枚举类因为默认的类修饰符为 final 所以也不能被派生(继承),同理枚举类也不能为当作实现。
2.枚举类自身可以实现接口,既可以进行统一实现重写接口抽象方法,也可以按照枚举类型单个实现重写。
2、枚举类的定义
关于枚举类的定义,这块主要想和大家分享两种方式
- jdk 5.0之前,自定义枚举类方式
- jdk 5.0之后,Enum关键字方式定义
3、实践
(一)、准备工作
我们新建一个 Java Project ,并创建一个包,以及一个测试类
(二)、自定义枚举的三种方式(jdk 5.0 之前)
1. 定义一个抽象类,在抽象类中定义常量进行维护,我们接下来以 Java 类库中的 Calendar 类示例来进行说明
新建一个类 EnumDemo01.java 代码如下:
package org.taoguoguo;
import java.util.Calendar;
/**
* @author taoGG
* @description jdk 5.0 之前 抽象类枚举方案Demo
* @create 2020-09-13 14:20
*/
public class EnumDemo01 {
public static void main(String[] args) {
Calendar calendar = Calendar.getInstance();
System.out.println(calendar.get(1));
}
}
Console 结果输出:
2020
Process finished with exit code 0
如果熟悉 Calendar API 的小伙伴 应该马上能反应过来,这个是获取当前的年份,类似的值还有
3 - 一年中的第几个星期
4 - 一年中的第几个月
5 - 当前的日期
......
但是这么多值,我们怎么能记得住呢?万一我输入错误,随便取了一个范围怎么办?
没错,这是 jdk 5.0之前的痛点,为了解决实例数量固定,便于维护这些问题,在jdk 5.0之后更新Enum枚举类解决了这个问题。那在jdk 5.0之前官方是怎么做的呢?难道需要我们一个个去记住 Calendar 的数字?
实际上官方本身,采用的就是我们现在说的第一种方式,在抽象类中定义常量进行维护
现在我们将代码做些修改:
package org.taoguoguo;
import java.util.Calendar;
/**
* @author taoGG
* @description jdk 5.0 之前 抽象类枚举方案Demo
* @create 2020-09-13 14:20
*/
public class EnumDemo01 {
public static void main(String[] args) {
Calendar calendar = Calendar.getInstance();
System.out.println(calendar.get(Calendar.YEAR));
}
}
我们运行进行输出:
2020
Process finished with exit code 0
结果与之前一致,这时我们就清楚,在开发过程中作为开发者我们肯定愿意使用 Calendar.YEAR 这种写法,一来方便记忆,二来可读性高。那么官方的做法时怎样的呢?我们点进去源码看一下
- 首先 Calendar 本身是一个抽象类,实现了序列化、克隆、以及比较排序接口,这边和我们枚举没有太大关系,我们继续往下看
- 在抽象类中,定义了很多个静态常量进行维护,而当我们需要使用时,直接调用,这样就比我们写一个个的具体值要方便和易用了。
2. 定义一个接口,在接口中定义常量维护枚举值
我们新建一个interface CustomerInf.java
package org.taoguoguo;
/**
* @author taoGG
* @description 接口常量维护枚举值
* @create 2020-09-13 15:47
*/
public interface CustomerInf {
int RED = 1;
int GREEN = 2;
int BLUE = 3;
}
在 EnumTest 进行测试
package org.taoguoguo;
/**
* @author taoGG
* @description Java枚举测试类
* @create 2020-09-13 14:54
*
*/
public class EnumTest {
public static void main(String[] args) {
System.out.println(CustomerInf.RED);
}
}
测试结果:
1
Process finished with exit code 0
这种做法我们达到了和在抽象类中维护常量相同的目的。上面这两种做法都非常的简单易用,但也有弊端。比如我们只知道一个状态值,当我们要获取状态的属性或者相关的内容时,我们该怎么做呢?
下面我们使用第三种方式,自定义枚举类,这种基本上达到和 Enum 关键字相同的作用,但有一点不足就是会较为复杂
3.自定义枚举类,通过为类私有化构造器和固定实例对象进行枚举维护
新建一个class SeasonEnum.java,代码如下:
package org.taoguoguo;
/**
* @author taoGG
* @description
* @create 2020-09-13 15:58
*/
public class SeasonEnum {
//1.声明枚举对象的属性
private final String seasonName;
private final int code;
//2.私有化类的构造器
private SeasonEnum(String seasonName,int code){
this.seasonName = seasonName;
this.code = code;
}
//3.提供当前枚举类的多个对象 public static final
public static final SeasonEnum SPRING = new SeasonEnum("春天",100);
public static final SeasonEnum SUMMER = new SeasonEnum("夏天",200);
public static final SeasonEnum AUTUMN = new SeasonEnum("秋天",300);
public static final SeasonEnum WINTER = new SeasonEnum("冬天",400);
//4.为类提供获取属性的方法
public String getSeasonName() {
return seasonName;
}
public int getCode() {
return code;
}
//5.重写toString方法
@Override
public String toString() {
return "SeasonEnum{" +
"seasonName='" + seasonName + '\'' +
", code=" + code +
'}';
}
}
新建一个class SeasonEnumTest 进行测试,当我们通过自定义枚举类引用实例对象时,如下图可以看到,我们已经可以获取到我们的枚举对象了。
获取到枚举对象,我们当然也可以获取到对应的属性及方法,这种可用性就提高了很多,我们在开发程序进行判断,可以根据各种枚举值的指定属性来进行,提高了代码的可维护性。
SeasonEnumTest 测试代码
package org.taoguoguo;
/**
* @author taoGG
* @description
* @create 2020-09-13 16:04
*/
public class SeasonEnumTest {
public static void main(String[] args) {
SeasonEnum spring = SeasonEnum.SPRING;
System.out.println("自定义枚举类对象:" + spring);
System.out.println("自定义枚举类属性:" + spring.getSeasonName());
System.out.println("自定义枚举类属性:" + spring.getCode());
}
}
根据我们上面的自定义枚举类方式,我们基本已经实现了枚举的功能了,但是就像上面说到的,如果开发中枚举类型较多,开发多个这样的自定义枚举类会非常的耗时,所以 jdk 5.0 之后,推出了 Enum 关键字定义枚举类
(三)Enum 关键字定义枚举类(jdk 5.0之后)
enum 全称为 enumeration,是jdk 5.0 中引入的新特性,在Java 中被 enum 关键字修饰的类型就是枚举类型
我们通过代码来示例来讲解和理解 enum 的用法,还是用我们刚刚自定以枚举类的例子,看看使用enum如何来写
新建一个Java class ,Kind 类型选择 enum 如图:
枚举类创建注意:
- 枚举实例必须在 enum关键字声明的类中显式的指定(首行开始的以第一个分号结束)
- 枚举不允许使用new,clone,反射,序列化手动创建枚举实例
package org.taoguoguo;
/**
* @author taoGG
* @description
* @create 2020-09-13 16:23
*/
public enum Season {
SPRING("春天",100),
SUMMER("夏天",200),
AUTUMN("秋天",300),
WINTER("冬天",400);
private final String seasonName;
private final int code;
Season(String seasonName, int code){
this.seasonName = seasonName;
this.code = code;
}
public String getSeasonName() {
return seasonName;
}
public int getCode() {
return code;
}
}
使用 SeasonTest 测试类进行测试:
package org.taoguoguo;
/**
* @author taoGG
* @description
* @create 2020-09-13 16:27
*/
public class SeasonTest {
public static void main(String[] args) {
Season spring = Season.SPRING;
System.out.println(spring);
}
}
输出结果:
SPRING
Process finished with exit code 0
注意,在enmu 枚举类中如果没有重写 toString方法,会默认使用Enum类本身提供的 toString 方法,返回枚举类名称,因为定义的枚举类默认隐式继承于java.lang.Enum
1.枚举类主要方法介绍
- values() :该方法可以返回当前枚举类型的对象数组,可以很方便的遍历所有枚举值。一般我们可以根据枚举类的相关属性通过此方法遍历获取对应的枚举对象及枚举值
- valueOf(String str) : 根据枚举类名称获取枚举类对象
- toString(): 默认使用 java.lang.Enum的 toString方法,返回当前对象常量的名称,枚举类推荐重写返回自定义友好描述
- name(): 返回当前枚举对象名称,和toString作用上类似,当时toString支持重写,name方法是不能重写的,在本质上 toString 也是调用的 name方法,枚举定义 name 方法就是为了返回枚举对象名称,而 toString 应该根据需要进行重写
- ordinal(): 返回当前枚举对象的序号, 实现了 Comparable 接口,表明它是支持排序的 可以通过 Collections.sort 进行自动排序比较此枚举与指定对象的顺序
- compareTo(): 基于ordinal进行序号大小比较
方式演示代码,小伙伴们可以自行运行输出一下,看看各个方法的作用,熟悉一下相关的方法api
package org.taoguoguo;
/**
* @author taoGG
* @description
* @create 2020-09-13 16:27
*/
public class SeasonTest {
public static void main(String[] args) {
System.out.println("========values()方法=======");
for (Season season : Season.values()) {
System.out.println(season);
}
System.out.println("===========================");
System.out.println("========valueOf方法========");
Season spring = Season.valueOf("SPRING");
System.out.println(spring);
System.out.println("===========================");
System.out.println("========toString方法========");
System.out.println(spring.toString());
System.out.println("===========================");
System.out.println("========name方法========");
System.out.println(spring.name());
System.out.println("===========================");
System.out.println("========ordinal方法========");
System.out.println(spring.ordinal());
System.out.println("===========================");
System.out.println("========compareTo方法========");
System.out.println(spring.compareTo(Season.WINTER));
System.out.println("===========================");
}
}
2.枚举类对接口的实现方式
准备工作
新建一个EnumInf 接口,定义一个抽象方法
package org.taoguoguo;
/**
* @author taoGG
* @description
* @create 2020-09-13 17:25
*/
public interface EnumInf {
void show();
}
1.实现接口,在enum中统一实现抽象方法
新建一个EnumInf 接口,定义抽象方法 show()
package org.taoguoguo;
/**
* @author taoGG
* @description
* @create 2020-09-13 17:25
*/
public interface EnumInf {
void show();
}
新建一个OrderStatus 枚举类 实现 EnumInf 接口
package org.taoguoguo;
/**
* @author taoGG
* @description
* @create 2020-09-13 17:27
*/
public enum OrderStatus implements EnumInf{
SUCCESS(200,"交易成功"),
Fail(500,"交易失败");
private final int code;
private final String desc;
OrderStatus(int code, String desc){
this.code = code;
this.desc = desc;
}
public int getCode() {
return code;
}
public String getDesc() {
return desc;
}
/**
* 第一种方式,枚举统一重写接口抽象方法
*/
@Override
public void show() {
System.out.println("订单枚举对象");
}
}
进行测试
package org.taoguoguo;
/**
* @author taoGG
* @description
* @create 2020-09-13 17:32
*/
public class OrderStatusTest {
public static void main(String[] args) {
OrderStatus success = OrderStatus.SUCCESS;
success.show();
}
}
输出结果
订单枚举对象
Process finished with exit code 0
跟我们常用类实现没有什么区别,枚举也是可以统一实现的,那如果想针对不同的枚举对象进行不同状态的实现怎么办呢?比如我们的OA系统、或者电商系统中,根据不同状态 我们需要回写对应的数据,下面我们就来看看如何实现。
2.枚举对象分别实现接口中的抽象方法
案例跟接口统一实现一致,我们这边修改一下OrderStatus 枚举类,代码如下
package org.taoguoguo;
/**
* @author taoGG
* @description
* @create 2020-09-13 17:27
*/
public enum OrderStatus implements EnumInf{
SUCCESS(200,"交易成功") {
@Override
public void show() {
System.out.println("回写交易成功状态");
}
},
Fail(500,"交易失败") {
@Override
public void show() {
System.out.println("回写交易失败状态");
}
};
private final int code;
private final String desc;
OrderStatus(int code, String desc){
this.code = code;
this.desc = desc;
}
public int getCode() {
return code;
}
public String getDesc() {
return desc;
}
}
我们再修改下测试类代码:
package org.taoguoguo;
/**
* @author taoGG
* @description
* @create 2020-09-13 17:32
*/
public class OrderStatusTest {
public static void main(String[] args) {
OrderStatus success = OrderStatus.SUCCESS;
success.show();
OrderStatus fail = OrderStatus.Fail;
fail.show();
}
}
输出结果
回写交易成功状态
回写交易失败状态
Process finished with exit code 0
通过这种方式就可以轻而易举地定义每个枚举实例不同的行为方式,也达到了我们预期的效果,其实在开发过程中根据枚举的设计和设计模式的铺垫可以极大的简化我们的业务代码。
3.Enum枚举类的工具类及应用场景
1.EnumSet 和 EnumMap
Java 中提供了两个方便操作enum的工具类——EnumSet 和 EnumMap。
EnumSet 是枚举类型的高性能 Set 实现。它要求放入它的枚举常量必须属于同一枚举类型。
// EnumSet的使用
System.out.println("EnumSet展示");
EnumSet<OrderStatus> errSet = EnumSet.allOf(OrderStatus.class);
for (OrderStatus e : errSet) {
System.out.println(e.name() + " : " + e.ordinal());
}
EnumMap 是专门为枚举类型量身定做的 Map 实现。虽然使用其它的 Map 实现(如HashMap)也能完成枚举类型实例到值得映射,但是使用 EnumMap 会更加高效:它只能接收同一枚举类型的实例作为键值,并且由于枚举类型实例的数量相对固定并且有限,所以 EnumMap 使用数组来存放与枚举类型对应的值。(计算机处理连续的资源使用局部内存效率更高)这使得 EnumMap 的效率非常高。
// EnumMap的使用
System.out.println("EnumMap展示");
EnumMap<StateMachine.Signal, String> errMap = new EnumMap(StateMachine.Signal.class);
errMap.put(StateMachine.Signal.RED, "红灯");
errMap.put(StateMachine.Signal.YELLOW, "黄灯");
errMap.put(StateMachine.Signal.GREEN, "绿灯");
for (Iterator<Map.Entry<StateMachine.Signal, String>> iter =errMap.entrySet().iterator(); iter.hasNext();) {
Map.Entry<StateMachine.Signal, String> entry = iter.next();
System.out.println(entry.getKey().name() + " : " + entry.getValue());
}
2.枚举类与 Switch 的配合使用
关于枚举与switch是个比较简单的话题,使用switch进行条件判断时,条件参数一般只能是整型,字符型。而枚举型确实也被switch所支持,在java 1.7后switch也对字符串进行了支持。
实践
新建一个 BizEnum 的java class,代码如下
package org.taoguoguo;
/**
* @author taoGG
* @description 企业类型枚举
* @create 2020-09-13 21:24
*/
public enum BizEnum {
COUNTRIES(101,"国有企业"),
PRIVETE(102,"私营企业"),
SOHO(103,"个体单位");
private final int code;
private final String desc;
BizEnum(int code, String desc){
this.code = code;
this.desc = desc;
}
public int getCode() {
return code;
}
public String getDesc() {
return desc;
}
//根据编码获取当前枚举对象的方法
public static BizEnum getBizTypeByCode(int code){
for (BizEnum bizEnum : BizEnum.values()) {
if(code == bizEnum.getCode()){
return bizEnum;
}
}
return null;
}
}
结合Switch进行测试
package org.taoguoguo;
/**
* @author taoGG
* @description
* @create 2020-09-13 21:31
*/
public class BizTest {
public static void main(String[] args) {
BizEnum bizType = BizEnum.getBizTypeByCode(101);
switch (bizType){
case COUNTRIES:
System.out.println("国有企业");
break;
case PRIVETE:
System.out.println("私营企业");
break;
case SOHO:
System.out.println("个体单位");
break;
default:
System.out.println("创业中");
}
}
}
输出结果:
国有企业
Process finished with exit code 0
总结
- jdk 5.0之前我们可以自定义枚举类,jdk 5.0之后使用enum关键字定义枚举类,枚举类默认继承自java.lang.Enum,使用枚举类将常量组织起来,便于统一管理。例如错误码、状态机等场景中,较为合适使用枚举类。
- 枚举类常用方法介绍及枚举类实现抽象类、接口等抽象方法的两种方式。
- 枚举常用的工具类及与switch使用的场景。
相关推荐
- 电脑装系统用GHOST好,还是原装版本好?老司机都是这么装的
-
Hello大家好,我是兼容机之家的咖啡。安装Windows系统是原版ISO好还是ghost好呢?针对这个的问题,我们先来科普一下什么是ghost系统,和原版ISO镜像两者之间有哪些优缺点。如果是很了解...
- 苹果 iOS 14.5.1/iPadOS 14.5.1 正式版发布
-
IT之家5月4日消息今日凌晨,苹果发布了iOS14.5.1与iPadOS14.5.1正式版更新。这一更新距iOS14.5正式版发布过去了一周时间。IT之家了解到,苹果表示,...
- iOS 13.1.3 正式版发布 包含错误修复和改进
-
苹果今天发布了iOS13.1.3和iPadOS13.1.3,这是iOS13发布之后第四个升级补丁。iOS13.1.2两周前发布。iOS13.1.3主要包括针对iPad和...
- 还不理解 Error 和 Exception 吗,看这篇就够了
-
在Java中的基本理念是结构不佳的代码不能运行,发现错误的理想时期是在编译期间,因为你不用运行程序,只是凭借着对Java基本理念的理解就能发现问题。但是编译期并不能找出所有的问题,有一些N...
- Linux 开发人员发现了导致 MacBook“无法启动”的 macOS 错误
-
“多个严重”错误影响配备ProMotion显示屏的MacBookPro。...
- 启动系统时无法正常启动提示\windows\system32\winload.efi
-
启动系统时无法正常启动提示\windows\system32\winload.efi。该怎么解决? 最近有用户遇到了开机遇到的问题,是Windows未能启动。原因可能是最近更改了硬件或软件。虽然提...
- 离线部署之两种构建Ragflow镜像的方式,dify同理
-
在实际项目交付过程中,经常遇到要离线部署的问题,生产服务器无法连接外网,这时就需要先构建好ragflow镜像,然后再拷到U盘或刻盘,下面介绍两种构建ragflow镜像的方式。性能测试(网络情况好的情况...
- Go语言 error 类型详解(go语言 异常)
-
Go语言的error类型是用于处理程序运行中错误情况的核心机制。它通过显式的返回值(而非异常抛出)来管理错误,强调代码的可控性和清晰性。以下是详细说明及示例:一、error类型的基本概念内置接口...
- Mac上“闪烁的问号”错误提示如何修复?
-
现在Mac电脑的用户越来越多,Mac电脑在使用过程中也会出现系统故障。当苹果电脑无法找到系统软件时,Mac会给出一个“闪烁的问号”的标志。很多用户受到过闪烁问号这一常见的错误提示的影响,如何解决这个问...
- python散装笔记——177 sys 模块(python sys模块详解)
-
sys模块提供了访问程序运行时环境的函数和值,例如命令行参数...
- 30天自制操作系统:第一天(30天自制操作系统电子书)
-
因为咱们的目的是为了研究操作系统的组成,所以直接从系统启动的第二阶段的主引导记录开始。前提是将编译工具放在该文件目录的同级目录下,该工具为日本人川合秀实自制的编译程序,优化过的nasm编译工具。...
- 五大原因建议您现在不要升级iOS 13或iPadOS
-
今天苹果放出了iPadOS和iOS13的公测版本,任何对新版功能感兴趣的用户都可以下载安装参与测试。除非你想要率先体验Dark模式,以及使用AppleID来登陆Facebook等服务,那么外媒CN...
- Python安装包总报错?这篇解决指南让你告别pip烦恼!
-
在Python开发中,...
- 苹果提供了在M1 Mac上修复macOS重装错误的方案
-
#AppleM1芯片#在苹果新的M1Mac推出后不久,我们看到有报道称,在这些机器上恢复和重新安装macOS,可能会导致安装错误,使你的Mac无法使用。具体来说,错误信息如下:"An...
- 黑苹果卡代码篇三:常见卡代码问题,满满的干货
-
前言...
你 发表评论:
欢迎- 一周热门
- 最近发表
-
- 电脑装系统用GHOST好,还是原装版本好?老司机都是这么装的
- 苹果 iOS 14.5.1/iPadOS 14.5.1 正式版发布
- iOS 13.1.3 正式版发布 包含错误修复和改进
- 还不理解 Error 和 Exception 吗,看这篇就够了
- Linux 开发人员发现了导致 MacBook“无法启动”的 macOS 错误
- 启动系统时无法正常启动提示\windows\system32\winload.efi
- 离线部署之两种构建Ragflow镜像的方式,dify同理
- Go语言 error 类型详解(go语言 异常)
- Mac上“闪烁的问号”错误提示如何修复?
- python散装笔记——177 sys 模块(python sys模块详解)
- 标签列表
-
- idea eval reset (50)
- vue dispatch (70)
- update canceled (42)
- order by asc (53)
- spring gateway (67)
- 简单代码编程 贪吃蛇 (40)
- transforms.resize (33)
- redisson trylock (35)
- 卸载node (35)
- np.reshape (33)
- torch.arange (34)
- node卸载 (33)
- npm 源 (35)
- vue3 deep (35)
- win10 ssh (35)
- exceptionininitializererror (33)
- vue foreach (34)
- idea设置编码为utf8 (35)
- vue 数组添加元素 (34)
- std find (34)
- tablefield注解用途 (35)
- python str转json (34)
- java websocket客户端 (34)
- tensor.view (34)
- java jackson (34)