枚举实现原理

news/2024/11/19 8:48:29/文章来源:https://www.cnblogs.com/seven97-top/p/18387584

枚举的定义

在JDK1.5之前,我们要是想定义一些有关常量的内容,例如定义几个常量,表示从周一到周末,一般都是在一个类,或者一个接口中,写类似于如下代码:

public class WeekDayConstant {public static final int MONDAY = 0;public static final int TUESDAY = 1;public static final int WEDNESDAY = 2;public static final int THURSDAY = 3;//...
}

这样做也可以实现功能,有几个缺点:

  • 各个常量的值可能会一样,出现混淆,例如不小心把TUESDAY 定义为0
  • 使用起来并不是很方便,例如想要获取某一种枚举的所有枚举值列表,根名称获取值等,还要去编码实现
  • 并不是很安全,例如反射修改常量的值,MONDAY 的值可能被修改为1
  • 方式并不是很优雅

为了不重复造轮子,Java在JDK1.5的时候,引入了枚举enum关键字(enum就是enumeration的缩写),我们可以定义枚举类型。

访问修饰符 enum 枚举类型名称{//一个或多个枚举值定义,一般采用大写加下划线的方式,用英文逗号分隔,例如,A,B,C;//在最后一个枚举值后面建议加一个分号,对于与只有枚举值的枚举定义来说,可以没有分号//后面就是一些方法的定义
}

例如,周一到周末的枚举定义:

public enum WeekDay {MONDAY,TUESDAY,WEDNESDAY,THURSDAY,FRIDAY,SATURDAY,SUNDAY;
}

可以单独定义一个文件,也可以定义在其他类的文件中。

枚举的实现原理

先从简单的来,我现在把我们上面使用的Subject枚举类改成下面的样子:

public enum Subject {CHINESE,MATH,ENGLISH;
}

切换到class所在的目录,然后:

javap -c Subject.class

javap是JDK自带的反汇编工具,得到下面的结果:

Compiled from "Subject.java"
public final class com.victory.test.object_size.Subject extends java.lang.Enum<com.victory.test.object_size.Subject> {public static final com.victory.test.object_size.Subject CHINESE;public static final com.victory.test.object_size.Subject MATH;public static final com.victory.test.object_size.Subject ENGLISH;public static com.victory.test.object_size.Subject[] values();Code:0: getstatic     #1                  // Field $VALUES:[Lcom/victory/test/object_size/Subject;3: invokevirtual #2                  // Method "[Lcom/victory/test/object_size/Subject;".clone:()Ljava/lang/Object;6: checkcast     #3                  // class "[Lcom/victory/test/object_size/Subject;"9: areturnpublic static com.victory.test.object_size.Subject valueOf(java.lang.String);Code:0: ldc           #4                  // class com/victory/test/object_size/Subject2: aload_03: invokestatic  #5                  // Method java/lang/Enum.valueOf:(Ljava/lang/Class;Ljava/lang/String;)Ljava/lang/Enum;6: checkcast     #4                  // class com/victory/test/object_size/Subject9: areturnstatic {};Code:0: new           #4                  // class com/victory/test/object_size/Subject3: dup4: ldc           #7                  // String CHINESE6: iconst_07: invokespecial #8                  // Method "<init>":(Ljava/lang/String;I)V10: putstatic     #9                  // Field CHINESE:Lcom/victory/test/object_size/Subject;13: new           #4                  // class com/victory/test/object_size/Subject16: dup17: ldc           #10                 // String MATH19: iconst_120: invokespecial #8                  // Method "<init>":(Ljava/lang/String;I)V23: putstatic     #11                 // Field MATH:Lcom/victory/test/object_size/Subject;26: new           #4                  // class com/victory/test/object_size/Subject29: dup30: ldc           #12                 // String ENGLISH32: iconst_233: invokespecial #8                  // Method "<init>":(Ljava/lang/String;I)V36: putstatic     #13                 // Field ENGLISH:Lcom/victory/test/object_size/Subject;39: iconst_340: anewarray     #4                  // class com/victory/test/object_size/Subject43: dup44: iconst_045: getstatic     #9                  // Field CHINESE:Lcom/victory/test/object_size/Subject;48: aastore49: dup50: iconst_151: getstatic     #11                 // Field MATH:Lcom/victory/test/object_size/Subject;54: aastore55: dup56: iconst_257: getstatic     #13                 // Field ENGLISH:Lcom/victory/test/object_size/Subject;60: aastore61: putstatic     #1                  // Field $VALUES:[Lcom/victory/test/object_size/Subject;64: return
}

解释:

  • 从反编译的代码可以看出编译器确实帮我们生成了一个Subject类,该类继承自java.lang.Enum类,生成的这个类是用final修饰的,因此枚举不能被继承,也不能继承其他类了,但是可以实现接口。

  • 声明的每一个枚举常量,都是Subject类的一个实例(见前面几行),这充分说明了前面使用关键字enum定义的类型中的每种Subject枚举常量也是实实在在的Subject实例对象,只不过代表的内容不一样而已。

  • 编译器还生成了两个静态方法,分别是values()和 valueOf(),

  • 接着是编译器生成的静态代码块,后面是静态代码块的JVM指令

到此也就明白了,使用关键字enum定义的枚举类型,在编译期后,也将转换成为一个实实在在的类,而在该类中,会存在每个在枚举类型中定义好变量的对应实例对象,同时编译器会为该类创建两个方法,分别是values()和valueOf()。

枚举的常用方法

  • name():是一个实例方法,该方法在java.lang.Enum中,返回枚举的名称,枚举的名称就是定义枚举常量时用的字符串,该方法被final修饰,因此不能被重写
  • values():编译器生成的static方法,按照声明的顺序返回枚举类中定义的所有枚举常量组成的数组,这个方法是一个隐含的方法,由编译器生成的。
  • valueOf(String):编译器生成的static方法,它根据一个名称返回一个枚举常量。
    如果名称所表示的枚举常量不存在,则抛出java.lang.IllegalArgumentException异常。这个方法是一个隐含的方法,由编译器生成的,对于一个具体的枚举类来说,这个方法是有的,但是java.lang.Enum中没有这个方法。
  • valueOf(Class,String):是一个静态的方法,存在于java.lang.Enum中,它的作用跟上一个方法类似,只不过第一个参数是Class类型的,需要指定获取那个类型的常量,第二个参数是常量的名称。
  • getDeclaringClass() :是一个实例方法,存在于java.lang.Enum中,可以获取代表当前枚举类型的Class对象,被final关键字修饰,不能被重写
  • ordinal():是一个实例方法,返回当前枚举常量的序号,序号是在枚举类中声明的顺序,从0开始,最大值是java.lang.Integer.MAX_VALUE,被final关键字修饰,不能被重写。如果枚举常量中的声明位置发生变化,那么ordinal方法获取到的值也随之变化,注意在Effective item35中认为大多数情况下都不应该使用该方法,毕竟它总是变幻莫测的。
  • toString():是一个实例方法,来自于java.lang.Object,在java.lang.Enum的实现是直接返回了name属性,就是name()方法的返回值,这个方法可以被重写。
  • compareTo(E):是一个实例方法,java.lang.Enum类实现了Comparable接口,用于比较当前枚举实例和指定的枚举实例,如果两个枚举实例的类型都不一样,直接会怕抛出异常,否则比较的是他们的ordinal值,这个值是ordinal()方法的返回值,由于这个方法由final修饰,因此不能被重写。

关于作者

来自一线程序员Seven的探索与实践,持续学习迭代中~

本文已收录于我的个人博客:https://www.seven97.top

公众号:seven97,欢迎关注~

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

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

相关文章

航图中的扇区数据生成

今天简单聊一下机场扇区,最后再介绍一下《风标设计2025》航图模块中的扇区绘制功能。机场扇区是以机场基准点(ARP)或归航台为圆心,半径46km(25nm),外加9km(5nm)缓冲区构成。PBN程序扇区通常以ARP为圆心,传统程序扇区以导航台为圆心。对于中小机场,为了统一和简化扇区划设…

【Azure Policy】添加策略用于审计Azure 网络安全组(NSG)规则 -- 只能特定的IP地址允许3389/22端口访问

问题描述 对Azure上的虚拟机资源,需要进行安全管理。只有指定的IP地址才能够通过RDP/SSH远程到虚拟机上, 有如下几点考虑: 1) 使用Azure Policy服务,扫描订阅中全部的网络安全组(NSG: Network Security Group) 资源 2) 判断入站规则,判断是否是3389, 22端口 3) 判断源地…

动态规划——数字三角形模型

数字三角形模型 母题 : 数字三角形思路 ​ 集合 f [i] [j] 表示所有从起点走到(i,j)的路径 ​ 属性 f [i] [j] 存的数是集合中所有路径和的最大值 ​ 状态计算:对于每一条从起点到 ( i , j ) 的路径,其要么是从左上方来的,要么是从右上方来的。 #include<bits/stdc++…

盘点一个Pycharm汉化过程中的一个坑

大家好,我是Python进阶者。 一、前言 前几天在Python最强白银交流群【生产队的驴都是不歇的】问了一个Python汉化的问题,问题如下:要把我弄疯了,汉化不成功,想重新安装,又不给出卸载选项,通过硬盘安装插件又提示异常。去插件文件夹删除所有插件文件,再重新安装pycharm,…

深度解析:OCR技术在企业HR管理中的应用

HR管理的挑战 企业的HR部门在处理员工入职、资料审核、信息录入存储管理等环节面临着大量重复性劳动力的问题。而HR部门通常人手有限,传统的人工录入方式不仅效率低下,而且容易出错。OCR技术的引入,极大地起到了降本增效地目的。应用的办公场景人脸比对技术 应用背景:在员工…

.NET 摄像头采集

本文主要介绍摄像头(相机)如何采集数据,用于类似摄像头本地显示软件,以及流媒体数据传输场景如传屏、视讯会议等。 摄像头采集有多种方案,如AForge.NET、WPFMediaKit、OpenCvSharp、EmguCv、DirectShow.NET、MediaCaptre(UWP),网上一些文章以及github已经有很多介绍,这…

Python深度学习股价预测、量化交易策略:LSTM、GRU深度门控循环神经网络|附代码数据

全文链接:https://tecdat.cn/?p=37539 原文出处:拓端数据部落公众号 分析师:Shuo Zhang 本文以上证综指近 22 年的日交易数据为样本,构建深度门控循环神经网络模型,从股价预测和制定交易策略两方面入手,量化循环神经网络在股票预测以及交易策略中的效果,结合一个Pytho…

Windos NTP 服务设定

1、修改注册表2, 双击 打开 设为自动 应用 保存

【专题】中国游戏产业AIGC发展前景报告合集PDF分享(附原数据表)

原文链接:https://tecdat.cn/?p=37535 原文出处:拓端数据部落公众号 近八成头部游戏企业在人工智能、数字孪生、引擎开发、云技术和XR等技术领域布局;有近六成头部游戏企业已构建AI生产管线、赋能虚拟内容生产或智能营销;此外,国内TOP50游戏厂商投资AI企业已超百次。阅读原…

自动化测试:Monkey工具实践应用~

在移动应用的自动化测试中,意外的用户操作和各种不可预见的场景往往是导致应用崩溃的主要原因。如何有效地模拟这些复杂场景,成为了测试工程师的一大挑战。而在这一过程中,Monkey工具凭借其随机化测试的独特优势,成为了许多团队的利器。 那么,Monkey工具究竟是如何帮助测试…

TCP/IP、UDP/IP协议

参考链接 1、OSI七层模型 (1)OSI含义“OSI模型,即开放式通信系统互联参考模型(Open System Interconnection Reference Model),是国际标准化组织(ISO)提出的一个试图使各种计算机在世界范围内互连为网络的标准框架,简称OSI。”(2)OSI定义了网络互连的七层模型(物理…