java 枚举(enum) 全面解读

枚举类型是单例模式的。你需要实例化一次,然后再整个程序之中就可以调用他的方法和成员变量了。
枚举类型使用单例模式是因为他的值是固定的,不需要发生改变。

简介

枚举是Java1.5引入的新特性,通过关键字enum来定义枚举类。枚举类是一种特殊类,它和普通类一样可以使用构造器、定义成员变量和方法,也能实现一个或多个接口,但枚举类不能继承其他类.

原理分析

枚举类型使用的最常用类型就是枚举常量.下面通过一个简单的Demo来说明枚举的原理.

272a58b95e47c547e18635fcfc8ffd2b.png

这样只是能够知道枚举简单的使用方法,不能看出枚举的特点和枚举的具体实现.

下面我们通过 jad工具来反编译Color类, 通过jad -sjava Color.class反编译出一份java文件.

0c68b9aa0b9ad0dabbdf8e6a132be295.png

从反编译的类中,可以看出, 我们使用enum关键字编写的类,在编译阶段编译器会自动帮我们生成一份真正在jvm中运行的代码.

该类继承自 Enum类,public abstract class Enum>implements Comparable, Serializable.

Enum类接受一个继承自Enum的泛型.(在反编译java文件中没有体现泛型是因为,泛型在阶段就会被类型类型擦除,替换为具体的实现.).

从反编译的Color类中可以看出,在enum关键字的类中,第一行 **(准确的说是第一个分号前)**定义的变量,都会生成一个 Color实例,且它是在静态域中进行初始化的, 而静态域在类加载阶段的cinit中进行初始化,所以枚举对象是线程安全的,由JVM来保证.

生成的枚举类有 **Color $VALUES[];**成员变量,外部可以通过values()方法获取当前枚举类的所有实例对象.

Enum成员变量和方法分析

7ed88fd6c21e224437160d196362e5c9.png

Enum成员变量

Enum成员变量和方法

Enum类实现了 Comparable接口,表明它是支持排序的,可以通过 Collections.sort 进行自动排序.实现了**public final int compareTo(E o)**接口,方法定义为final且其实现依赖的ordinal字段也是final类型,说明他只能根据ordinal排序,排序规则不可变.

8ae2815bfab6e34b3d12c33d129883ef.png

ordinal: 表示枚举的顺序,从Color类中可以看出,它是从0开始按自然数顺序增长,且其值是final类型,外部无法更改.对于 ordinal()方法,官方建议尽量不要使用它,它主要是提供给EnumMap,EnumSet使用的.

7740d19503d5b6a8b5667a18f8a3fdcf.png

name: 表示枚举类的名字,从Color类的构造函数可以看出,它的值就是我们定义的实例的名称.

我们在例子中之所以能打印出实例名称,是因为 它的toString()方法直接返回了name属性.

3a7f1f9fb88097f3830858ba50001747.png

equals(): 从其实现来看, 我们程序中使用 == 或者 equals来判断两个枚举相等都是一样的.

28f757f2fd0824181b926ed44c73f2c3.png

getDeclaringClass(): 方法返回枚举声明的Class对象

每一个枚举类型极其定义的枚举变量在JVM中都是唯一的

这句话的意思是枚举类型它拥有的实例在编写的时候,就已经确定下,不能通过其他手段进行创建,且枚举变量在jvm有且只有一个对应的实例.

为了达到这个效果,它通过以下方法来确保.

1. 类加载时创建,保证线程安全

从Color类中可以看出, Color对象是在静态域创建,由类加载时初始化,JVM保证线程安全,这样就能确保Color对象不会因为并发同时请求而错误的创建多个实例.

2. 对序列化进行特殊处理,防止反序列化时创建新的对象

我们知道一旦实现了Serializable接口之后,反序列化时每次调用 readObject()方法返回的都是一个新创建出来的对象.

而枚举则不同,在序列化的时候Java仅仅是将枚举对象的name属性输出到结果中,反序列化的时候则是通过Enum的valueOf()方法来根据名字查找枚举对象。同时,编译器是不允许任何对这种序列化进行定制,因此禁用了writeObjectreadObjectreadObjectNoDatawriteReplacereadResolve等方法。

1883ee0896a0743324f7b8b2b5e91a26.png

1.私有构造函数, 无法正常的 new出对象

b243c6f95f4b5315f42dac0395ec17ba.png

2.无法通过 clone()方法,克隆对象

655c8354ccd5f395519e8ed38e32fbcf.png

3. 无法通过反射的方式创建枚举对象

枚举类型,在 JVM 层面禁止了通过反射构造枚举实例的行为,如果尝试通过反射创建,将会报Cannot reflectively create enum objects.

178876789f45899b70258054b56076c4.png

枚举类的特点总结

  1. 枚举实例必须在 enum关键字声明的类中显式的指定(首行开始的以第一个分号结束)
  2. 除了1, 没有任何方式(new,clone,反射,序列化)可以手动创建枚举实例
  3. 枚举类不可被继承
  4. 枚举类是线程安全的
  5. 枚举类型是类型安全的(typesafe)
  6. 无法继承其他类(已经默认继承Enum)

枚举的使用

  • 枚举常量

如上诉 Color枚举类,就是典型的枚举常量.

它可以在 switch语句中使用

a23d4b07b9fca7b4bd76280d2dc37458.png

枚举类型是类型安全的,可以对传入的值进行类型检查:

如有个 handleColor(Color color)方法,那么方法参数自动会对类型进行检查,只能传入 Color.WHITEColor.BLACK,如果使用 static final定义的常量则不具备 类型安全的特点.

  • 枚举与构造函数

枚举类可以编写自己的构造函数,但是不能声明public,protected,为了是不让外部创建实例对象,默认为private且只能为它.

  • 枚举与类

除了枚举常量外, enum是一个完整的类,它也可以编写自己的构造方法以及方法,甚至实现接口.

这里需要注意,枚举类不能继承其他类,因为在编译时它已经继承了 Enum,java无法多继承

1d3dfebcfdd79346acfb6f3b493900ad.png

枚举与单例模式

单例模式网上有6-7中写法,除了 枚举方式外, 都有两个致命的缺点, 不能完全保证单例在jvm中保持唯一性.

1. 反射创建单例对象

解决方案 : 在构造上述中判断,当多于一个实例时,再调用构造函数,直接报错.

2. 反序列化时创建对象

解决方案 : 使用**readResolve()**方法来避免此事发生.

这两种缺点虽然都有方式解决,但是不免有些繁琐.

枚举类天生有这些特性.而且实现单例相当简单.

5079fde186959fd4714bae6029ff6cc5.png

所以,枚举实现的单例,可以说是最完美和简洁的单例了.推荐大家使用这种方式创建单例.

但是,枚举类的装载和初始化时会有时间和空间的成本. 它的实现比其他方式需要更多的内存空间,所以在Android这种受资源约束的设备中尽量避免使用枚举单例,而选择 双重检查锁(DCL)和静态内部类的方式实现单例.

枚举与策略模式

特定的常量类型与主体中的方法或行为有关时,即当数据与行为之间有关联时,可以考虑使用枚举来实现策略模式.

如我们需要实现加减运算,就可以在枚举类型中声明一个 apply抽象方法,在特定于常量的方法(Constant-specific class body的Constant -specific method implementation)中,用具体实现抽象方法.

ff6e974319b860f2ed660679d5112c43.png

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

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

相关文章

修改表名_面试官:如何批量修改mysql表字段、表、数据库字符集和排序规则

概述目前数据库字符集统一用的utf8,由于项目需要,引进了表情,但是utf8mb5才支持表情字符,所以需统一修改数据库字符集,下面介绍批量修改数据库字符集的办法。修正顺序是字段级别>表级别>库级别。一、批量修改整个…

Maven命令 install 和 package的区别

Maven命令 install 和 package的区别 Maven是目前十分流行的项目构建工具以及依赖解决工具,其提供的常用指令中有两个很容易引起使用者的疑惑, 那就是 install 和 package , 那么这两个命令到底有啥区别呢? Maven install 安装…

如何重启_消费市场按下重启键,企业该如何提前布局

2020广发卡携手企业和消费者,共同按下重启键,让我们放下包袱,轻松前行。当疫情结束后,你想做什么?也许是去见想见的人,和他一起去吃想吃的美食;也许是约上三五好友,或带着最亲的家人…

Linux中使用netstat命令的基本操作,排查端口号的占用情况

Linux中netstat命令详解 Netstat是控制台命令,是一个监控TCP/IP网络的非常有用的工具,它可以显示路由表、实际的网络连接以及每一个网络接口设备的状态信息。Netstat用于显示与IP、TCP、UDP和ICMP协议相关的统计数据,一般用于检验本机各端口的网络连接情…

与context的关系_Android-Context

一.简介Context 翻译为上下文环境,是一个应用程序环境信息的接口。如果以 Android 系统角度来看可以理解为某一与操作系统的交互的具体场景,比如 Activity 的具体功能,Service 的后台运行等。如果以程序的角度看,Context 是一个抽…

Linux中sudo、su和su -命令的区别

Linux中sudo、su和su -命令的区别小结 我们知道,在Linux下对很多文件进行修改都需要有root(管理员)权限,比如对/ect/profile等文件的修改。下面这篇文章主要给大家总结介绍了关于Linux中sudo、su和su -命令的区别的相关资料&…

如何根据进程号去查端口号?

1.查出进程号 eg: ps -ef |grep conet- 2.根据当前进程号获得端口号: eg: netstat -anp |grep 4118 得到当前的端口是8761

Linux中如何查看某个端口是否被占用的方法

LINUX中如何查看某个端口是否被占用的方法 之前查询端口是否被占用一直搞不明白,现在总结下: 1.netstat -anp |grep 端口号 如下,我以3306为例,netstat -anp |grep 3306(此处备注下,我是以普通用户操作&…

深入理解synchronized底层原理

文章目录前言一、synchronized的特性1.1 原子性1.2 可见性1.3 有序性1.4 可重入性二、synchronized的用法三、synchronized锁的实现3.1 同步方法3.2 同步代码块四、synchronized锁的底层实现五、JVM对synchronized的优化5.1 锁膨胀5.1.1 偏向锁5.1.2 轻量级锁5.1.3 重量级锁5.2…

centos7重新加载服务的命令_CentOS7 从查看、启动、停止服务说起systemctl

执行命令“systemctl status 服务名.service”可查看服务的运行状态,其中服务名后的.service 可以省略,这是CenOS7以后采用systemd作为初始化进程后产生的变化。Systemctl是一个systemd工具,主要负责控制systemd系统和服务管理器。Systemd是一…

一体化住户调查_曲麻莱县2020年城乡一体化住户调查表彰会暨年报部署会

为全面推进我县城乡一体化住户调查工作,总结经验、鼓励优秀,提高统计员和辅助调查员的工作积极性,提高账本数据质量,11月25日下午,县统计局组织召开2020年全县城乡一体化住户调查工作表彰会暨年报部署会。全县6个镇的统…

电力系统继电保护第二版张保会_电力系统继电保护试题

一、填空题(每小题1分,共20分)1.电气元件配置两套保护,一套保护不动作时另一套保护动作于跳闸,称为_近后备_保护。2.电流继电器的_返回_电流与动作电流的比值称为继电器的返回系数。3.反应电流增大而瞬时动作的保护称为__无时限电流速断保护_…

Linux中shell脚本详解

文章目录1、第一个脚本程序:2、shell获取字符串长度:3、shell变量:4、**引用shell变量**:5、shell变量的赋值、修改、删除:5、shell特殊变量:6、shell中字符串的拼接:**7、字符串的截取**8、she…

java递归实现多级菜单栏_Java构建树形菜单以及支持多级菜单的实例代码

这篇文章主要介绍了Java构建树形菜单的实例代码(支持多级菜单),非常不错,具有参考借鉴价值,需要的朋友可以参考下效果图:支持多级菜单。菜单实体类:public class Menu {// 菜单idprivate String id;// 菜单名称private String nam…

java中clone方法的理解(深拷贝、浅拷贝)

文章目录前言:知识点一:什么是浅拷贝?知识点二:什么是深拷贝?知识点三、java拷贝(clone)的前提:知识点四:浅拷贝案例:拷贝类:测试类:总…

mysql实现内容加密_简单为mysql 实现透明加密方法

一般用户在数据库中保存数据,虽然数据库存储的是二进制,无法直接明文打开查看,但是如果是一个外行人,直接连接进入mysql中,还是可以直接查看数据的。所以对于一些核心数据,特别是企业重要数据资产&#xff…

Java之AQS(AbstractQueuedSynchronizer)

Java之AQS(AbstractQueuedSynchronizer) AQS 介绍 AQS 的全称为 AbstractQueuedSynchronizer ,翻译过来的意思就是抽象队列同步器。这个类在 java.util.concurrent.locks 包下面。 ● 是用来实现锁或者其他同步器组件的公共基础部分的抽象实…

SpringBoot 3.0最低版本要求的JDK 17,这几个新特性不能不知道

最近,有很多人在传说 SpringBoot要出3.0的版本了,并且宣布不再支持 Java 8,最低要求是 Java 17了。 其实,早在2021年9月份,关于 Spring Framework 6.0的消息出来的时候,Spring 官方就已经明确了不会向下兼…

jmeter mysql数据导出_Jmeter连接mysql

一、下载添加jar包image.png添加方法:1.拷贝到jmeter/lib目录下,此方法需重启jmeter2.直接在jmeter的测试计划中导入image.png二、连接mysql数据库添加配置元件-JDBC Connection Configurationimage.pngimage.png1.Variable Name for created pool&#…

判断一个坐标点是否在不规则多边形内部的算法

参考:https://wrf.ecse.rpi.edu//Research/Short_Notes/pnpoly.html 在GIS(地理信息管理系统)中,判断一个坐标是否在多边形内部是个经常要遇到的问题。乍听起来还挺复杂。根据W. Randolph Franklin 提出的PNPoly算法,…