Java枚举深度解读,看这篇就够了
ztj100 2024-12-05 18:08 19 浏览 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使用的场景。
相关推荐
- 其实TensorFlow真的很水无非就这30篇熬夜练
-
好的!以下是TensorFlow需要掌握的核心内容,用列表形式呈现,简洁清晰(含表情符号,<300字):1.基础概念与环境TensorFlow架构(计算图、会话->EagerE...
- 交叉验证和超参数调整:如何优化你的机器学习模型
-
准确预测Fitbit的睡眠得分在本文的前两部分中,我获取了Fitbit的睡眠数据并对其进行预处理,将这些数据分为训练集、验证集和测试集,除此之外,我还训练了三种不同的机器学习模型并比较了它们的性能。在...
- 机器学习交叉验证全指南:原理、类型与实战技巧
-
机器学习模型常常需要大量数据,但它们如何与实时新数据协同工作也同样关键。交叉验证是一种通过将数据集分成若干部分、在部分数据上训练模型、在其余数据上测试模型的方法,用来检验模型的表现。这有助于发现过拟合...
- 深度学习中的类别激活热图可视化
-
作者:ValentinaAlto编译:ronghuaiyang导读使用Keras实现图像分类中的激活热图的可视化,帮助更有针对性...
- 超强,必会的机器学习评估指标
-
大侠幸会,在下全网同名[算法金]0基础转AI上岸,多个算法赛Top[日更万日,让更多人享受智能乐趣]构建机器学习模型的关键步骤是检查其性能,这是通过使用验证指标来完成的。选择正确的验证指...
- 机器学习入门教程-第六课:监督学习与非监督学习
-
1.回顾与引入上节课我们谈到了机器学习的一些实战技巧,比如如何处理数据、选择模型以及调整参数。今天,我们将更深入地探讨机器学习的两大类:监督学习和非监督学习。2.监督学习监督学习就像是有老师的教学...
- Python 模型部署不用愁!容器化实战,5 分钟搞定环境配置
-
你是不是也遇到过这种糟心事:花了好几天训练出的Python模型,在自己电脑上跑得顺顺当当,一放到服务器就各种报错。要么是Python版本不对,要么是依赖库冲突,折腾半天还是用不了。别再喊“我...
- 神经网络与传统统计方法的简单对比
-
传统的统计方法如...
- 自回归滞后模型进行多变量时间序列预测
-
下图显示了关于不同类型葡萄酒销量的月度多元时间序列。每种葡萄酒类型都是时间序列中的一个变量。假设要预测其中一个变量。比如,sparklingwine。如何建立一个模型来进行预测呢?一种常见的方...
- 苹果AI策略:慢哲学——科技行业的“长期主义”试金石
-
苹果AI策略的深度原创分析,结合技术伦理、商业逻辑与行业博弈,揭示其“慢哲学”背后的战略智慧:一、反常之举:AI狂潮中的“逆行者”当科技巨头深陷AI军备竞赛,苹果的克制显得格格不入:功能延期:App...
- 时间序列预测全攻略,6大模型代码实操
-
如果你对数据分析感兴趣,希望学习更多的方法论,希望听听经验分享,欢迎移步宝藏公众号...
你 发表评论:
欢迎- 一周热门
- 最近发表
- 标签列表
-
- 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)
- npm 源 (35)
- vue3 deep (35)
- win10 ssh (35)
- 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)
- vmware17pro最新密钥 (34)
- mysql单表最大数据量 (35)