Coder,我怀疑你并不会枚举
枚举是JDK1.5引入的新特性。被enum
关键字修饰的类就是一个枚举类。
关于枚举,阿里巴巴开发手册有这样两条建议:
枚举类名带上 Enum 后缀,枚举成员名称需要全大写,单词间用下划线隔开。
如果变量值仅在一个固定范围内变化用 enum 类型来定义。
一 枚举类有哪些特点
创建一个ColorEnum
的枚举类,通过编译,再反编译看看它发生了哪些变化。
public enum ColorEnum {
RED,GREEN,BULE;
}
使用命令javac ColorEnum.java
进行编译生成class文件,然后再用命令javap -p ColorEnum.class
进行反编译。
去掉包名,反编译后的内容如下:
public final class ColorEnum extends Enum{
public static final ColorEnum GREEN;
public static final ColorEnum BULE;
private static final ColorEnum[] $VALUES;
public static ColorEnum[] values();
public static ColorEnum valueOf(java.lang.String);
private ColorEnum();
static {};
}
枚举类被
final
修饰,因此枚举类不能被继承;枚举类默认继承了
Enum
类,java不支持多继承,因此枚举类不能继承其他类;枚举类的构造器是
private
修饰的,因此其他类不能通过构造器来获取对象;枚举类的成员变量是
static
修饰的,可以用类名.变量来获取对象;values()方法是获取所有的枚举实例;
valueOf(java.lang.String)是根据名称获取对应的实例;
二 枚举创建线程安全的单例模式
public enum SingletonEnum {
INSTANCE;
public void doSomething(){
// dosomething...
}
}
这样一个单例模式就创建好了,通过SingletonEnum.INSTANCE
来获取对象就可以了。
2.1 序列化造成单例模式不安全
一个类如果如果实现了序列化接口,则可能破坏单例。每次反序列化一个序列化的一个实例对象都会创建一个新的实例。
枚举序列化是由JVM
保证的,每一个枚举类型和定义的枚举变量在JVM
中都是唯一的,在枚举类型的序列化和反序列化上,Java做了特殊的规定:在序列化时Java仅仅是将枚举对象的name属性输出到结果中,反序列化的时候则是通过java.lang.Enum
的valueOf
方法来根据名字查找枚举对象。同时,编译器是不允许任何对这种序列化机制的定制的并禁用了writeObject
、readObject
、readObjectNoData
、writeReplace
和readResolve
等方法,从而保证了枚举实例的唯一性。
2.2 反射造成单例模式不安全
通过反射强行调用私有构造器来生成实例对象,造成单例模式不安全。
Class<?> aClass = Class.forName("xx.xx.xx");
Constructor<?> constructor = aClass.getDeclaredConstructor(String.class);
SingletonEnum singleton = (SingletonEnum) constructor.newInstance("Java旅途");
但是使用枚举创建的单例完全不用考虑这个问题,来看看newInstance
的源码!
public T newInstance(Object ... initargs)
throws InstantiationException, IllegalAccessException,
IllegalArgumentException, InvocationTargetException
{
if (!override) {
if (!Reflection.quickCheckMemberAccess(clazz, modifiers)) {
Class<?> caller = Reflection.getCallerClass();
checkAccess(caller, clazz, null, modifiers);
}
}
// 如果是枚举类型,直接抛出异常,不让创建实例对象!
if ((clazz.getModifiers() & Modifier.ENUM) != 0)
throw new IllegalArgumentException("Cannot reflectively create enum objects");
ConstructorAccessor ca = constructorAccessor; // read volatile
if (ca == null) {
ca = acquireConstructorAccessor();
}
@SuppressWarnings("unchecked")
T inst = (T) ca.newInstance(initargs);
return inst;
}
如果是enum
类型,则直接抛出异常Cannot reflectively create enum objects
,无法通过反射创建实例对象!
三 通过枚举消除if/else
假如要写一套加密接口,分别给小程序、app和web端来使用,但是这三种客户端的加密方式不一样。一般情况下我们会传一个类型type
来判断来源,然后调用对应的解密方法即可。代码如下:
if("WEIXIN".equals(type)){
// dosomething
}else if("APP".equals(type)){
// dosomething
}else if("WEB".equals(type)){
// dosomething
}
现在使用枚举来消除这些if/else。
写一个加密用的接口,有加密和解密两个方法。然后用不同的算法去实现这个接口完成加解密。
public interface Util {
// 解密
String decrypt();
// 加密
String encrypt();
}
创建一个枚举类来实现这个接口
public enum UtilEnum implements Util {
WEIXIN {
@Override
public String decrypt() {
return "微信解密";
}
@Override
public String encrypt() {
return "微信加密";
}
},
APP {
@Override
public String decrypt() {
return "app解密";
}
@Override
public String encrypt() {
return "app加密";
}
},
WEB {
@Override
public String decrypt() {
return "web解密";
}
@Override
public String encrypt() {
return "web加密";
}
};
}
最后,获取到type后,直接调用解密方法就行了。
String decryptMessage = UtilEnum.valueOf(type).decrypt();
以后,如果新增了一个其他加密方式,只需要修改上面的枚举类就完成了,业务代码都不需要改动。
这就是枚举类比较高级的两个用法。
本文分享自微信公众号 - Java旅途(Javatrip)。
如有侵权,请联系 support@oschina.cn 删除。
本文参与“OSC源创计划”,欢迎正在阅读的你也加入,一起分享。
低调大师中文资讯倾力打造互联网数据资讯、行业资源、电子商务、移动互联网、网络营销平台。
持续更新报道IT业界、互联网、市场资讯、驱动更新,是最及时权威的产业资讯及硬件资讯报道平台。
转载内容版权归作者及来源网站所有,本站原创内容转载请注明来源。
- 上一篇
细数软件工程各阶段必不可少的那些图
一、前言 软件工程中规定,软件生命周期由软件定义、软件开发和运行维护(也称为软件维护)3个时期组成,每一个时期又进一步划分为若干个阶段。 软件定义时期包括问题定义、可行性研究、需求分析三个阶段。 软件开发时期包括总体设计、详细设计、编码和单元测试、综合测试四个阶段。 软件维护时期只包括软件维护这一个阶段。 本文旨在说明在软件生命周期不同阶段的各种图的含义与使用。 二、可行性研究 2.1 系统流程图 系统流程图是概括地描绘物理系统的传统工具。它的基本思想是用图形符号以黑盒子形式描绘组成系统的每个部件(程序、文档、数据库、人工过程等)。系统流程图表达的数据在系统各部件之间流动的情况,而不是对数据进行加工处理的控制过程,因此尽管系统流程图的某些符号和程序流程图的符号形式相同,但是它却是物理数据流图而不是程序流程图。 符号 当以概括的方式抽象的描绘一个实际系统时,仅仅使用图2.1中列出的基本符号就足够了。当需要更具体地描绘一个物理系统时还需要使用图2.2中列出的系统符号,利用这些符号可以把一个广义的输入输出操作具体化为读写存储在特殊设备上的文件(或数据库),把抽象处理具体化为特定的程序或手工操...
- 下一篇
你还不知道 OpenGL ES 和 EGL 的关系?
什么是 EGL EGL 是 OpenGL ES 和本地窗口系统(Native Window System)之间的通信接口 ,它的主要作用: 与设备的原生窗口系统通信; 查询绘图表面的可用类型和配置; 创建绘图表面; 在OpenGL ES 和其他图形渲染API之间同步渲染; 管理纹理贴图等渲染资源。 OpenGL ES 的平台无关性正是借助 EGL 实现的,EGL 屏蔽了不同平台的差异(Apple 提供了自己的 EGL API 的 iOS 实现,自称 EAGL)。 本地窗口相关的 API 提供了访问本地窗口系统的接口,而 EGL 可以创建渲染表面 EGLSurface ,同时提供了图形渲染上下文 EGLContext,用来进行状态管理,接下来 OpenGL ES 就可以在这个渲染表面上绘制。 egl、opengles 和设备之间的关系 图片中: Display(EGLDisplay) 是对实际显示设备的抽象; Surface(EGLSurface)是对用来存储图像的内存区域 FrameBuffer 的抽象,包括 Color Buffer(颜色缓冲区), Stencil Buffer(模板...
相关文章
文章评论
共有0条评论来说两句吧...
文章二维码
点击排行
推荐阅读
最新文章
- CentOS7编译安装Gcc9.2.0,解决mysql等软件编译问题
- SpringBoot2配置默认Tomcat设置,开启更多高级功能
- SpringBoot2更换Tomcat为Jetty,小型站点的福音
- Linux系统CentOS6、CentOS7手动修改IP地址
- CentOS7编译安装Cmake3.16.3,解决mysql等软件编译问题
- CentOS关闭SELinux安全模块
- CentOS8编译安装MySQL8.0.19
- CentOS7,CentOS8安装Elasticsearch6.8.6
- SpringBoot2编写第一个Controller,响应你的http请求并返回结果
- Jdk安装(Linux,MacOS,Windows),包含三大操作系统的最全安装