枚舉是JDK1.5引入的新特性。被enum
關鍵字修飾的類就是一個枚舉類。java
關於枚舉,阿里巴巴開發手冊有這樣兩條建議:web
建立一個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
修飾的,能夠用類名.變量來獲取對象;public enum SingletonEnum { INSTANCE; public void doSomething(){ // dosomething... } }
這樣一個單例模式就建立好了,經過SingletonEnum.INSTANCE
來獲取對象就能夠了。微信
一個類若是若是實現了序列化接口,則可能破壞單例。每次反序列化一個序列化的一個實例對象都會建立一個新的實例。app
枚舉序列化是由JVM
保證的,每個枚舉類型和定義的枚舉變量在JVM
中都是惟一的,在枚舉類型的序列化和反序列化上,Java作了特殊的規定:在序列化時Java僅僅是將枚舉對象的name屬性輸出到結果中,反序列化的時候則是經過java.lang.Enum
的valueOf
方法來根據名字查找枚舉對象。同時,編譯器是不容許任何對這種序列化機制的定製的並禁用了writeObject
、readObject
、readObjectNoData
、writeReplace
和readResolve
等方法,從而保證了枚舉實例的惟一性。ide
經過反射強行調用私有構造器來生成實例對象,形成單例模式不安全。學習
Class<?> aClass = Class.forName("xx.xx.xx"); Constructor<?> constructor = aClass.getDeclaredConstructor(String.class); SingletonEnum singleton = (SingletonEnum) constructor.newInstance("Java旅途");
可是使用枚舉建立的單例徹底不用考慮這個問題,來看看newInstance
的源碼!ui
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
,沒法經過反射建立實例對象!
假如要寫一套加密接口,分別給小程序、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旅途」進行關注。回覆「1024」便可得到學習視頻及精美電子書。天天7:30準時推送技術文章,讓你的上班路不在孤獨,並且每個月還有送書活動,助你提高硬實力!