Coder,我怀疑你并不会枚举

开发 后端
关于枚举,阿里巴巴开发手册有这样两条建议:枚举类名带上 Enum 后缀,枚举成员名称需要全大写,单词间用下划线隔开。

[[337601]]

枚举是JDK1.5引入的新特性。被enum关键字修饰的类就是一个枚举类。

关于枚举,阿里巴巴开发手册有这样两条建议:

枚举类名带上 Enum 后缀,枚举成员名称需要全大写,单词间用下划线隔开。

如果变量值仅在一个固定范围内变化用 enum 类型来定义。

一 枚举类有哪些特点

创建一个ColorEnum的枚举类,通过编译,再反编译看看它发生了哪些变化。

  1. public enum ColorEnum { 
  2.     RED,GREEN,BULE; 

使用命令javac ColorEnum.java进行编译生成class文件,然后再用命令javap -p ColorEnum.class进行反编译。

 

去掉包名,反编译后的内容如下:

  1. public final class ColorEnum extends Enum{ 
  2.     public static final ColorEnum GREEN; 
  3.     public static final ColorEnum BULE; 
  4.     private static final ColorEnum[] $VALUES
  5.     public static ColorEnum[] values(); 
  6.     public static ColorEnum valueOf(java.lang.String); 
  7.     private ColorEnum(); 
  8.     static {}; 
  1. 枚举类被final修饰,因此枚举类不能被继承;
  2. 枚举类默认继承了Enum类,java不支持多继承,因此枚举类不能继承其他类;
  3. 枚举类的构造器是private修饰的,因此其他类不能通过构造器来获取对象;
  4. 枚举类的成员变量是static修饰的,可以用类名.变量来获取对象;
  5. values()方法是获取所有的枚举实例;
  6. valueOf(java.lang.String)是根据名称获取对应的实例;

二 枚举创建线程安全的单例模式

  1. public enum  SingletonEnum { 
  2.  
  3.     INSTANCE; 
  4.  
  5.     public void doSomething(){ 
  6.         // dosomething... 
  7.     } 

这样一个单例模式就创建好了,通过SingletonEnum.INSTANCE来获取对象就可以了。

2.1 序列化造成单例模式不安全

一个类如果如果实现了序列化接口,则可能破坏单例。每次反序列化一个序列化的一个实例对象都会创建一个新的实例。

枚举序列化是由JVM保证的,每一个枚举类型和定义的枚举变量在JVM中都是唯一的,在枚举类型的序列化和反序列化上,Java做了特殊的规定:在序列化时Java仅仅是将枚举对象的name属性输出到结果中,反序列化的时候则是通过java.lang.Enum的valueOf方法来根据名字查找枚举对象。同时,编译器是不允许任何对这种序列化机制的定制的并禁用了writeObject、readObject、readObjectNoData、writeReplace和readResolve等方法,从而保证了枚举实例的唯一性。

2.2 反射造成单例模式不安全

通过反射强行调用私有构造器来生成实例对象,造成单例模式不安全。

  1. Class<?> aClass = Class.forName("xx.xx.xx"); 
  2. Constructor<?> constructor = aClass.getDeclaredConstructor(String.class); 
  3. SingletonEnum singleton = (SingletonEnum) constructor.newInstance("Java旅途"); 

但是使用枚举创建的单例完全不用考虑这个问题,来看看newInstance的源码!

  1. public T newInstance(Object ... initargs) 
  2.     throws InstantiationException, IllegalAccessException, 
  3. IllegalArgumentException, InvocationTargetException 
  4.     if (!override) { 
  5.         if (!Reflection.quickCheckMemberAccess(clazz, modifiers)) { 
  6.             Class<?> caller = Reflection.getCallerClass(); 
  7.             checkAccess(caller, clazz, null, modifiers); 
  8.         } 
  9.     } 
  10.     // 如果是枚举类型,直接抛出异常,不让创建实例对象! 
  11.     if ((clazz.getModifiers() & Modifier.ENUM) != 0) 
  12.         throw new IllegalArgumentException("Cannot reflectively create enum objects"); 
  13.     ConstructorAccessor ca = constructorAccessor;   // read volatile 
  14.     if (ca == null) { 
  15.         ca = acquireConstructorAccessor(); 
  16.     } 
  17.     @SuppressWarnings("unchecked"
  18.     T inst = (T) ca.newInstance(initargs); 
  19.     return inst; 

如果是enum类型,则直接抛出异常Cannot reflectively create enum objects,无法通过反射创建实例对象!

三 通过枚举消除if/else

假如要写一套加密接口,分别给小程序、app和web端来使用,但是这三种客户端的加密方式不一样。一般情况下我们会传一个类型type来判断来源,然后调用对应的解密方法即可。代码如下:

  1. if("WEIXIN".equals(type)){ 
  2.     // dosomething 
  3. }else if("APP".equals(type)){ 
  4.     // dosomething 
  5. }else if("WEB".equals(type)){ 
  6.     // dosomething 

现在使用枚举来消除这些if/else。

写一个加密用的接口,有加密和解密两个方法。然后用不同的算法去实现这个接口完成加解密。

  1. public interface Util { 
  2.  
  3.     // 解密 
  4.     String decrypt(); 
  5.  
  6.     // 加密 
  7.     String encrypt(); 

创建一个枚举类来实现这个接口

  1. public enum UtilEnum implements Util { 
  2.  
  3.     WEIXIN { 
  4.         @Override 
  5.         public String decrypt() { 
  6.             return "微信解密"
  7.         } 
  8.  
  9.         @Override 
  10.         public String encrypt() { 
  11.             return "微信加密"
  12.         } 
  13.     }, 
  14.     APP { 
  15.         @Override 
  16.         public String decrypt() { 
  17.             return "app解密"
  18.         } 
  19.  
  20.         @Override 
  21.         public String encrypt() { 
  22.             return "app加密"
  23.         } 
  24.     }, 
  25.     WEB { 
  26.         @Override 
  27.         public String decrypt() { 
  28.             return "web解密"
  29.         } 
  30.  
  31.         @Override 
  32.         public String encrypt() { 
  33.             return "web加密"
  34.         } 
  35.     }; 

最后,获取到type后,直接调用解密方法就行了。

  1. String decryptMessage = UtilEnum.valueOf(type).decrypt(); 

以后,如果新增了一个其他加密方式,只需要修改上面的枚举类就完成了,业务代码都不需要改动。

 

这就是枚举类比较高级的两个用法。

本文转载自微信公众号「Java旅途」,可以通过以下二维码关注。转载本文请联系Java旅途公众号。

 

责任编辑:武晓燕 来源: Java旅途
相关推荐

2019-03-29 15:13:59

数据中心Spiceworks服务器

2018-03-09 12:40:41

内存降价国产

2016-05-24 11:33:50

网络基础设施公有云软件即服务

2015-08-13 10:28:29

网络诈骗手机实名制

2013-05-10 09:17:23

2019-01-16 10:40:08

2016-03-03 15:00:50

智能记录数据

2020-06-24 09:00:43

分库分表MySQL

2020-05-14 08:59:28

API网关性能

2021-09-14 09:19:49

一号多卡手机卡号码

2021-12-31 18:24:45

ThreadLocal数据库对象

2017-10-31 10:12:12

无人驾驶安全性乘客信任

2020-11-01 17:00:04

重载重写java

2016-04-28 09:36:44

人才教育/华三

2020-02-22 21:45:00

TypeScriptJavaScript浏览器

2019-01-23 11:17:22

电脑程序员工作

2022-08-01 08:17:46

mysqlwindows系统

2019-07-01 14:44:23

Java互联网代码

2020-06-23 14:09:49

枚举JDK场景

2024-01-18 00:16:07

点赞
收藏

51CTO技术栈公众号