从初学者到专家:Java枚举的完整指南

1.枚举的概念

在Java中,枚举是一种特殊的数据类型,用于定义一组有限的命名常量。枚举提供了一种更直观、更可读的方式来表示一组相关的常量,并且可以为这些常量绑定其他数据或行为。

背景:枚举是在JDK1.5以后引入的。

主要用途是:将一组常量组织起来,在这之前表示一组常量通常使用定义常量的方式

要定义一个枚举,需要使用enum关键字。以下是一个简单的枚举定义的示例:

创建一个枚举类:TestEnum

public enum TestEnum {RED,        // 枚举常量 REDBLACK,      // 枚举常量 BLACKGREEN,      // 枚举常量 GREENWHITE;      // 枚举常量 WHITEpublic static void main(String[] args) {TestEnum testEnum2 = TestEnum.BLACK;   // 创建一个变量 testEnum2 并将其设置为 BLACK 枚举常量switch (testEnum2) {    // 使用 switch 语句根据 testEnum2 的值进行判断case RED:System.out.println("red");    // 如果 testEnum2 的值为 RED,则输出 "red"break;case BLACK:System.out.println("black");  // 如果 testEnum2 的值为 BLACK,则输出 "black"break;case WHITE:System.out.println("WHITE");  // 如果 testEnum2 的值为 WHITE,则输出 "WHITE"break;case GREEN:System.out.println("green");  // 如果 testEnum2 的值为 GREEN,则输出 "green"break;default:break;}}
}

2.枚举的常用方法

Enum类是所有枚举类型的基类,在Java中提供了一些常用的方法来操作和处理枚举类型。下面是
Enum类的常用方法:
1.values ()方法:
package demo2;public enum TestEnum {RED,        // 枚举常量 REDBLACK,      // 枚举常量 BLACKGREEN,      // 枚举常量 GREENWHITE;      // 枚举常量 WHITEpublic static void main(String[] args) {TestEnum[] values = TestEnum.values();for (TestEnum value : values) {System.out.println("Name: " + value.name() + ", Index: " + value.ordinal());}}
}

运行截图:


valueOf(String name)方法:

package demo2;public enum TestEnum {RED,        // 枚举常量 REDBLACK,      // 枚举常量 BLACKGREEN,      // 枚举常量 GREENWHITE;      // 枚举常量 WHITEpublic static void main(String[] args) {String colorName = "GREEN";TestEnum enumInstance = TestEnum.valueOf(colorName);System.out.println("Enum Constant Name: " + enumInstance.name());}
}

运行截图:


3.ordinal()方法:
package demo2;public enum TestEnum {RED,        // 枚举常量 REDBLACK,      // 枚举常量 BLACKGREEN,      // 枚举常量 GREENWHITE;      // 枚举常量 WHITEpublic static void main(String[] args) {TestEnum testEnum2 = TestEnum.BLACK;   // 创建一个变量 testEnum2 并将其设置为 BLACK 枚举常量int index = testEnum2.ordinal();   // 使用 ordinal() 方法获取 testEnum2 的索引位置System.out.println("Index: " + index);}
}

 运行截图:


4.compareTo()方法:

public enum TestEnum {RED,        // 枚举常量 REDBLACK,      // 枚举常量 BLACKGREEN,      // 枚举常量 GREENWHITE;      // 枚举常量 WHITEpublic static void main(String[] args) {TestEnum testEnum1 = TestEnum.RED;    // 创建一个变量 testEnum1 并将其设置为 RED 枚举常量TestEnum testEnum2 = TestEnum.BLACK;  // 创建一个变量 testEnum2 并将其设置为 BLACK 枚举常量int comparisonResult = testEnum1.compareTo(testEnum2);  // 使用 compareTo() 方法比较 testEnum1 和 testEnum2 的顺序if (comparisonResult < 0) {    // 如果 comparisonResult 小于 0System.out.println(testEnum1 + " comes before " + testEnum2);  // 输出 testEnum1 在 testEnum2 之前} else if (comparisonResult > 0) {   // 如果 comparisonResult 大于 0System.out.println(testEnum1 + " comes after " + testEnum2);   // 输出 testEnum1 在 testEnum2 之后} else {System.out.println(testEnum1 + " and " + testEnum2 + " are the same");  // 输出 testEnum1 和 testEnum2 相同}}
}

 运行截图:


3.枚举的构造方法

枚举类型在Java中可以包含构造方法,用于初始化枚举常量。枚举的构造方法是私有的,因为枚举常量在定义时就被实例化,并且不能在其他地方进行实例化。

同时:当枚举对象有参数后,需要提供相应的构造函数

代码案例1:

public enum TestEnum {RED("Red Color"),        // 枚举常量 REDBLACK("Black Color"),    // 枚举常量 BLACKGREEN("Green Color"),    // 枚举常量 GREENWHITE("White Color");    // 枚举常量 WHITEprivate String color;    // 枚举常量的属性private TestEnum(String color) {this.color = color;    // 枚举常量的构造方法}public String getColor() {return color;          // 获取枚举常量的颜色属性}
}

 

在上述代码中,TestEnum枚举类型包含了一个构造方法,它接受一个color参数,并将其赋值给枚举常量的color属性。构造方法是私有的,只能在枚举类型内部使用。

代码案例2:

public enum TestEnum {RED("Red Color", 1),        // 枚举常量 RED,具有颜色属性为 "Red Color" 和代码属性为 1BLACK("Black Color", 2),    // 枚举常量 BLACK,具有颜色属性为 "Black Color" 和代码属性为 2GREEN("Green Color", 3),    // 枚举常量 GREEN,具有颜色属性为 "Green Color" 和代码属性为 3WHITE("White Color", 4);    // 枚举常量 WHITE,具有颜色属性为 "White Color" 和代码属性为 4private String color;       // 枚举常量的颜色属性private int code;           // 枚举常量的代码属性private TestEnum(String color, int code) {this.color = color;       // 构造方法,用于初始化枚举常量的颜色属性和代码属性this.code = code;}public String getColor() {return color;             // 获取枚举常量的颜色属性}public int getCode() {return code;              // 获取枚举常量的代码属性}
}

在上述代码中,TestEnum枚举类型包含了一个接受两个参数的构造方法。每个枚举常量都会调用该构造方法进行初始化,并传递对应的参数。

除了构造方法,我们在枚举类型中定义了getColor()getCode()方法,用于获取枚举常量的颜色属性和代码属性。

代码案例3:

public enum TestEnum {RED("Red Color", 1, true),        // 枚举常量 REDBLACK("Black Color", 2, false),    // 枚举常量 BLACKGREEN("Green Color", 3, true),    // 枚举常量 GREENWHITE("White Color", 4, false);    // 枚举常量 WHITEprivate String color;       // 枚举常量的颜色属性private int code;           // 枚举常量的代码属性private boolean active;     // 枚举常量的活动状态属性private TestEnum(String color, int code, boolean active) {this.color = color;this.code = code;this.active = active;}public String getColor() {return color;}public int getCode() {return code;}public boolean isActive() {return active;}
}

在上述示例中,我们为枚举常量定义了一个带有三个参数的构造方法。每个枚举常量都会调用该构造方法进行初始化,并传递相应的参数。

除了构造方法,我们还定义了getColor()getCode()isActive()方法,用于获取枚举常量的颜色属性、代码属性和活动状态属性。

 

 4.枚举和反射

我们知道反射( 从初学者到专家:Java反射的完整指南-CSDN博客 )是,对于任何一个类,哪怕其构造方法是私有的,我们也可以通过反射拿到他的实例对象,那么枚举的构造方法也是私有的,我们是否可以拿到呢?
package demo2;import java.lang.reflect.Constructor;public enum TestEnum {RED("red",1),BLACK("black",2),WHITE("white",3),GREEN("green",4);private String name;private int key;private TestEnum (String name,int key) {this.name = name;this.key = key;}public static TestEnum getEnumKey (int key) {for (TestEnum t: TestEnum.values()) {if(t.key == key) {return t;}}return null;}public static void reflectPrivateConstructor() {try {Class<?> classStudent = Class.forName("TestEnum");
//注意传入对应的参数,获得对应的构造方法来构造对象,当前枚举类是提供了两个参数分别是String和int。Constructor<?> declaredConstructorStudent = classStudent.getDeclaredConstructor(String.class,int.class);
//设置为true后可修改访问权限declaredConstructorStudent.setAccessible(true);Object objectStudent = declaredConstructorStudent.newInstance("绿色",666);TestEnum testEnum = (TestEnum) objectStudent;System.out.println("获得枚举的私有构造函数:"+testEnum);} catch (Exception ex) {ex.printStackTrace();}}public static void main(String[] args) {reflectPrivateConstructor();}
}

运行截图:

这里的主要异常是: 就是没有对应的构造方法!
为什么呢?
因为我们提供的枚举的构造方法就是两个参数分别是 String int,我们所有的枚举类,都是默认继承与 java.lang.Enum ,说到继承,就要帮助父类进行构造!而我们写的类,并没有帮助父类构造!那意思是,我们要在自己的枚举类里面,提供super 吗?不是的,枚举比较特殊,虽然我们写的是两个,但是默认他还添加了两个参数哪两个参数呢?
我们看一下Enum 类的源码

 

 也就是说,我们自己的构造函数有两个参数一个是String一个是int,同时他默认后边还会给两个参数,一个是String一个是int。也就是说,这里我们正确给的是4个参数:

代码:

package demo2;import java.lang.reflect.Constructor;public enum TestEnum {RED("red",1),BLACK("black",2),WHITE("white",3),GREEN("green",4);private String name;private int key;private TestEnum (String name,int key) {this.name = name;this.key = key;}public static TestEnum getEnumKey (int key) {for (TestEnum t: TestEnum.values()) {if(t.key == key) {return t;}}return null;}public static void reflectPrivateConstructor() {try {Class<?> classStudent = Class.forName("demo2.TestEnum");
//注意传入对应的参数,获得对应的构造方法来构造对象,当前枚举类是提供了两个参数分别是String和int。Constructor<?> declaredConstructorStudent =classStudent.getDeclaredConstructor(String.class,int.class,String.class,int.class);
//设置为true后可修改访问权限declaredConstructorStudent.setAccessible(true);
//后两个为子类参数,大家可以将当前枚举类的key类型改为double验证Object objectStudent = declaredConstructorStudent.newInstance("父类参数",666,"子类参数",888);TestEnum testEnum = (TestEnum) objectStudent;System.out.println("获得枚举的私有构造函数:"+testEnum);} catch (Exception ex) {ex.printStackTrace();}}public static void main(String[] args) {reflectPrivateConstructor();}
}

运行截图:

但还是报错了,为什么?

 直接公布答案!

根据Java源代码, newInstance() 方法在创建实例之前会先检查目标类是否是一个枚举类型。如果目标类是枚举类型, newInstance() 方法会抛出 InstantiationException 异常,阻止通过反射去创建枚举实例。
有一道面试题就是关于: 为什么枚举实现单例模式是安全的?
有兴趣的小伙伴可以去了解一下

5.总结

以下是关于枚举的一些总结:

  1. 定义枚举:可以使用关键字‘enum’来定义枚举类型。枚举类型的定义通常位于类的顶层,可以包含枚举常量、字段、方法等。
  2. 枚举常量:枚举类型中的常量称为枚举常量。它们是枚举类型的实例,使用预定义的名称来表示特定的常量值。枚举常量在枚举类型的定义中以逗号分隔,并以大写字母命名。
  3. 枚举方法:枚举可以包含方法,可以为枚举类型添加自定义的行为。枚举方法可以在每个枚举常量上调用,并可以在枚举类型内部定义。
  4. 枚举的比较:枚举类型可以使用`==`运算符进行比较,因为每个枚举常量都是唯一的。
  5. 枚举的序列化:枚举类型默认是可序列化的,可以直接将枚举类型的对象进行序列化和反序列化操作。
  6. 枚举的限制:枚举常量在编译时就被确定,无法在运行时动态创建新的枚举常量。枚举常量的数量是固定的。
  7. 枚举的特性

  • 唯一性:枚举常量是唯一的,每个枚举常量在枚举类型中只会存在一个实例。
  • 不可变性:枚举常量是不可变的,一旦创建,其值无法修改。
  • 安全性:枚举常量在多线程环境下是安全的,不需要额外的同步措施。
  •  可迭代性:枚举类型可以使用`values()`方法获取包含所有枚举常量的数组,并支持使用增强的`for-each`循环进行遍历。

优点:

1. 类型安全性:枚举提供了类型安全性,编译器可以在编译时检查枚举类型的正确使用。枚举常量只能是预定义的值,不允许其他值的赋值,从而减少了错误的发生。

2. 可读性和可维护性:枚举常量使用预定义的名称来表示特定的常量值,这提供了更好的代码可读性。使用枚举可以使代码更加清晰、易于理解和维护。

3. 易于扩展:在需要添加新的常量时,可以简单地在枚举中定义新的枚举常量。这样可以方便地扩展现有的枚举类型,而不会影响到其他部分的代码。

4. 单例模式的简化:枚举本身就是单例模式的一种实现方式。枚举常量是唯一的,且在多线程环境下是安全的,不需要额外的同步措施。

缺点:

1. 限制了灵活性:枚举常量的数量是固定的,它们在编译时就被确定,无法在运行时动态创建新的枚举常量。这种限制可能会导致在某些特定的场景下,无法灵活地扩展枚举类型。

2. 不适合表示连续变化的值:枚举适用于表示一组固定的离散的常量值,但不适合表示连续变化的值。如果需要表示一系列连续变化的值,使用枚举可能会显得笨拙和不合适。

3. 可序列化的复杂性:枚举类型默认是可序列化的,但在某些情况下,当枚举类型需要进行序列化和反序列化时,可能会引起一些复杂性和不一致性的问题。

总体而言,枚举在许多情况下都是一种有用的工具,可以提供类型安全性、可读性和可维护性。然而,在一些特定的场景下,枚举的限制可能会导致不适合使用枚举,需要考虑其他的解决方案。

   

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/758815.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【JavaScript】JavaScript 程序流程控制 ② ( 循环流程控制 | 循环要素 - 循环体 / 循环终止条件 | for 循环语法结构 )

文章目录 一、JavaScript 程序流程控制 - 循环流程控制1、循环流程控制2、循环要素 - 循环体 / 循环终止条件3、for 循环语法结构 - 循环控制变量 / 循环终止条件 / 操作表达式4、for 循环 完整代码示例 一、JavaScript 程序流程控制 - 循环流程控制 1、循环流程控制 在 程序开…

杰发科技AC7801——Keil编译的Hex大小如何计算

编译结果是Keil里面前三个数据的总和&#xff1a; 即CodeRoDataRWData的总和。 通过ATCLinkTool工具查看内存&#xff0c;发现最后一个字节正好是5328 注意读内存数据时候需要强转成32位&#xff0c;加1000的 增加1024的地址只需要加256即可

关于paper中的一些硬件知识

一. OS中的event Information in event traces from software systems can help developers with performance analysis, debugging and troubleshooting 1.事件的概念 已知软件系统中的event能够帮助开发者对系统进行性能分析、调试以及定位&#xff0c;那我们应该仔细考虑…

突破图神经网络技术瓶颈!新阶段3大创新方向大幅提高模型性能

针对传统的图神经网络在处理非结构化数据、捕捉高阶关系等方面的局限性&#xff0c;研究者们提出了众多优化方案。 这其中&#xff0c;超图神经网络、几何图神经网络、动态图神经网络作为GNN发展的前沿方向&#xff0c;不仅提供了更加丰富和灵活的方法来处理各种复杂的图数据&…

【Linux】Linux安装软件---软件包管理器 yum

主页&#xff1a;醋溜马桶圈-CSDN博客 专栏&#xff1a;Linux_醋溜马桶圈的博客-CSDN博客 gitee&#xff1a;mnxcc (mnxcc) - Gitee.com 目录 1.Linux中安装软件 1.1 源代码安装 1.2 rpm包安装 1.3 yum安装 1.3.1 举例 1.3.2 图示yum下载安装 2.Linux系统的生态 如何选…

第9关:请将(S2,J6,P4,200)插入供应情况关系表SPJ

任务描述 请将(S2&#xff0c;J6&#xff0c;P4&#xff0c;200)插入供应情况关系表SPJ 相关知识 供应情况表SPJ由供应商代码(SNO)、零件代码(PNO)、工程项目代码(JNO)、供应数量(QTY)组成&#xff0c;标识某供应商 供应某种零件 给某工程项目的数量为QTY。 SPJ表如下图&…

单片机--数电(2)

组合逻辑电路 根基题目要求设计逻辑电路 组合逻辑电路 由一些逻辑门电路搭建&#xff0c;为实现某些功能的电路 特点 在任意时刻输出只取决于该时刻的输入&#xff0c;与电路原来的状态无关 根据图分析组合逻辑的方法 可以使用multisim的逻辑转换仪 1组合逻辑电路图 2…

CCIE-06-EIGRP_TS

目录 实验条件网络拓朴 路由器基础配置开始排错&#xff0c; 要求R14访问R11的lo0时负载均衡1. K值不匹配2. R14和R13邻居关系没有起来&#xff0c;3. 继续排查邻居关系&#xff0c;R13和R11关系没有起来4. R13的R11邻居关系起来又关闭&#xff0c;关闭又起来&#xff08;认证信…

【Qt学习笔记】(三)--编写上位机软件(ui设置、样式表serialport串口接收数据、Qchart显示波形)

声明&#xff1a;本人水平有限&#xff0c;博客可能存在部分错误的地方&#xff0c;请广大读者谅解并向本人反馈错误。    这段时间大部分都是在学Qt&#xff0c;前面想着跟着书一章章的学&#xff0c;但是发现这个效率极低&#xff0c;所以就改变了学习的方法&#xff0c;那…

蓝桥杯java组 螺旋折线

题目描述 如图所示的螺旋折线经过平面上所有整点恰好一次。 对于整点(X, Y)&#xff0c;我们定义它到原点的距离dis(X, Y)是从原点到(X, Y)的螺旋折线段的长度。 例如dis(0, 1)3, dis(-2, -1)9 给出整点坐标(X, Y)&#xff0c;你能计算出dis(X, Y)吗&#xff1f; 【输入格…

处理器方法的返回值

返回ModelAndView: 若处理器方法处理完后&#xff0c;需要跳转到其它资源&#xff0c;且又要在跳转的资源间传递数据&#xff0c;此时处理器方法返回ModelAndView 比较好。当然&#xff0c;若要返回 ModelAndView&#xff0c;则处理器方法中 需要定义ModelAndView对象。 在使用…

Python 深度学习第二版(GPT 重译)(四)

九、高级计算机视觉深度学习 本章涵盖 计算机视觉的不同分支&#xff1a;图像分类、图像分割、目标检测 现代卷积神经网络架构模式&#xff1a;残差连接、批量归一化、深度可分离卷积 可视化和解释卷积神经网络学习的技术 上一章通过简单模型&#xff08;一堆Conv2D和MaxP…

什么是高防CDN?

高防CDN&#xff08;Content Delivery Network&#xff0c;内容分发网络&#xff09;在网络安全中的作用非常重要。它通过一种特别的方式来保护网站和网络应用程序免受大规模DDoS攻击。以下是它的一些主要优势&#xff1a; 01 分布式防护 高防CDN通过在全球各地设立大量的节点…

深入解析Kafka中的动态更新模式

欢迎来到我的博客&#xff0c;代码的世界里&#xff0c;每一行都是一个故事 深入解析Kafka中的动态更新模式 前言动态更新模式的基础概念动态更新模式的概念&#xff1a;解决的问题和引入的原因&#xff1a; 原理解析与工作流程动态更新模式的工作原理和工作流程&#xff1a;示…

【联邦学习贡献评估——联邦学习优化】

1. 模型复用 贡献评估往往需要计算不同参与方组合的数据价值, 然而模型相关的价值度量指标, 比如测试准确率, 需要基于数据重新训练并评测模型, 这导致了高昂的数据价值度量代价. 为了避免重复训练联邦模型的代价, 考虑复用全体参与方组合下训练联邦模型时各参与方的梯度更新,…

【NTN 卫星通信】 车辆物联网设备通过NTN和TN切换的应用场景

1 场景描述 对于有两个3GPP无线接入网服务的大面积农田和农场&#xff0c;物联网设备可以通过NTN和TN接入网同时受益于5G系统的双转向数据连接能力。   在这个用例中&#xff0c;我们有一个广域的农业自动化应用系统来控制农业车辆&#xff0c;例如&#xff0c;一个装有数百个…

大模型提示学习样本量有玄机,自适应调节方法好

引言&#xff1a;探索文本分类中的个性化示例数量 在自然语言处理&#xff08;NLP&#xff09;领域&#xff0c;预测模型已经从零开始训练演变为使用标记数据对预训练模型进行微调。这种微调的极端形式涉及到上下文学习&#xff08;In-Context Learning, ICL&#xff09;&…

leetcode代码记录(删除字符串中的所有相邻重复项

目录 1. 题目&#xff1a;2. 我的代码&#xff1a;小结&#xff1a; 1. 题目&#xff1a; 给出由小写字母组成的字符串 S&#xff0c;重复项删除操作会选择两个相邻且相同的字母&#xff0c;并删除它们。 在 S 上反复执行重复项删除操作&#xff0c;直到无法继续删除。 在完成…

数据结构:9、二叉树

在上堆中已经介绍了什么是二叉树&#xff0c;所以这里直接写二叉树实现。 1、二叉树的构建 二叉树的构建第一步肯定是初始化&#xff0c;也就是构建这棵树&#xff0c;这里是利用前序遍历构建的&#xff0c;因为这里是利用链表形式创建的二叉树&#xff0c;所以这里就是和之前…

redis常用五大数据类型

目录 Key 字符串String 常用命令 列表List 常用命令 集合Set 常用命令 Hash哈希 键值对集合 有序集合Zset Redis新数据类型 Key set key value...添加keykeys *查看当前库中所有的keyexist key该key是否存在type keykey的类型del key删除keyunlink key根据value选择非阻塞…