单例设计模式-Enum枚举单例、原理源码解析以及反编译实战
生活随笔
收集整理的這篇文章主要介紹了
单例设计模式-Enum枚举单例、原理源码解析以及反编译实战
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
package com.learn.design.pattern.creational.singleton;/*** 這個類是Enum類型* 這個枚舉非常簡單* 枚舉類是Object* 他在多線程的時候呢* 肯定是沒有問題的* 我們主要是關注枚舉類型的序列號機制* 還有反射攻擊* 那枚舉類天然的可序列化機制呢* 能夠強有力的保證不會出現多次實例化的情況* 那即使在復雜的序列化* 或者反射的攻擊下* 枚舉類型的單例模式* 都沒有問題* 只不過我們現在這種寫法* 看起來不太自然* 但是枚舉類型的單例* 可能是實現單例模式中的最佳實現* 也是強烈推薦這種寫法* 那我們先測試一下序列化* 我們把序列化相關的代碼先打開* * * * @author Leon.Sun**/
public enum EnumInstance {/*** 我們聲明一個instance* */INSTANCE{protected void printTest(){System.out.println("Geely Print Test");}};protected abstract void printTest();/*** 為了測試我們聲明一個Object* 這里用Object* 用別的類型也可以* 只不過我們做測試使用* */private Object data;public Object getData() {return data;}public void setData(Object data) {this.data = data;}/*** 獲取這個枚舉類型* * @return*/public static EnumInstance getInstance(){return INSTANCE;}}
package com.learn.design.pattern.creational.singleton;import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.InvocationTargetException;public class Test {public static void main(String[] args) throws IOException, ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
// LazySingleton lazySingleton = LazySingleton.getInstance();// System.out.println("main thread"+ThreadLocalInstance.getInstance());
// System.out.println("main thread"+ThreadLocalInstance.getInstance());
// System.out.println("main thread"+ThreadLocalInstance.getInstance());
// System.out.println("main thread"+ThreadLocalInstance.getInstance());
// System.out.println("main thread"+ThreadLocalInstance.getInstance());
// System.out.println("main thread"+ThreadLocalInstance.getInstance());// Thread t1 = new Thread(new T());
// Thread t2 = new Thread(new T());
// t1.start();
// t2.start();
// System.out.println("program end");// HungrySingleton instance = HungrySingleton.getInstance();/*** 我們直接用EnumInstance獲取一個instance* 等于一個什么呢* EnumInstance.getInstance()* * */EnumInstance instance = EnumInstance.getInstance();/*** 我們直接setData* new一個Object* 也就是說枚舉類持有的對象在這一行已經new好了* 然后把它序列化* 再反序列化回來* 看看這個data是不是同一個* * */instance.setData(new Object());ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("singleton_file"));oos.writeObject(instance);File file = new File("singleton_file");ObjectInputStream ois = new ObjectInputStream(new FileInputStream(file));// HungrySingleton newInstance = (HungrySingleton) ois.readObject();/*** 這里替換成枚舉類型EnumInstance* * */EnumInstance newInstance = (EnumInstance) ois.readObject();/*** 輸出的時候是兩個instance* 并且他們是相等的* 那這些我們一會通過反編譯來看* 我們其實用的是枚舉類持有的對象* 那我們現在就用data來做實驗* * 所以我們這里邊直接打印data* * 第一個輸出原始的instance的data* java.lang.Object@3b07d329* * */System.out.println(instance.getData());/*** 反序列化回來的對象* 也是java.lang.Object@3b07d329* 他們想不想等呢* 答案是true* 那枚舉類就是這么強大* 那我們看一下序列號和反序列號對枚舉是怎么處理的呢* 我們打開ObjectInputStream這個類* */System.out.println(newInstance.getData());/*** 這個也是進行一個比較* 注意它是object類型* 我們直接run一下* 結果出來了* 我們一起來看一下* * */System.out.println(instance.getData() == newInstance.getData());// Class objectClass = HungrySingleton.class;
// Class objectClass = StaticInnerClassSingleton.class;// Class objectClass = LazySingleton.class;
// Class objectClass = EnumInstance.class;// Constructor constructor = objectClass.getDeclaredConstructor();
// Constructor constructor = objectClass.getDeclaredConstructor(String.class,int.class);
//
// constructor.setAccessible(true);
// EnumInstance instance = (EnumInstance) constructor.newInstance("Geely",666);//
// LazySingleton newInstance = (LazySingleton) constructor.newInstance();
// LazySingleton instance = LazySingleton.getInstance();// StaticInnerClassSingleton instance = StaticInnerClassSingleton.getInstance();
// StaticInnerClassSingleton newInstance = (StaticInnerClassSingleton) constructor.newInstance();// HungrySingleton newInstance = (HungrySingleton) constructor.newInstance();
// HungrySingleton instance = HungrySingleton.getInstance();// System.out.println(instance);
// System.out.println(newInstance);// System.out.println(instance == newInstance);// EnumInstance instance = EnumInstance.getInstance();
// instance.printTest();}
}
我們打開ObjectOutputStream這個類,這里面有一個方法叫readEnum/*** Reads in and returns enum constant, or null if enum type is* unresolvable. Sets passHandle to enum constant's assigned handle.*/private Enum<?> readEnum(boolean unshared) throws IOException {if (bin.readByte() != TC_ENUM) {throw new InternalError();}ObjectStreamClass desc = readClassDesc(false);if (!desc.isEnum()) {throw new InvalidClassException("non-enum class: " + desc);}int enumHandle = handles.assign(unshared ? unsharedMarker : null);ClassNotFoundException resolveEx = desc.getResolveException();if (resolveEx != null) {handles.markException(enumHandle, resolveEx);}String name = readString(false);Enum<?> result = null;Class<?> cl = desc.forClass();if (cl != null) {try {@SuppressWarnings("unchecked")Enum<?> en = Enum.valueOf((Class)cl, name);result = en;} catch (IllegalArgumentException ex) {throw (IOException) new InvalidObjectException("enum constant " + name + " does not exist in " +cl).initCause(ex);}if (!unshared) {handles.setObject(enumHandle, result);}}handles.finish(enumHandle);passHandle = enumHandle;return result;}看一下這個方法,我們看一下readEnum的依賴,那前面我們講序列號和反序列號的時候呢,講的是checkResolve(readOrdinaryObject(unshared))/*** Underlying readObject implementation.*/private Object readObject0(boolean unshared) throws IOException {boolean oldMode = bin.getBlockDataMode();if (oldMode) {int remain = bin.currentBlockRemaining();if (remain > 0) {throw new OptionalDataException(remain);} else if (defaultDataEnd) {/** Fix for 4360508: stream is currently at the end of a field* value block written via default serialization; since there* is no terminating TC_ENDBLOCKDATA tag, simulate* end-of-custom-data behavior explicitly.*/throw new OptionalDataException(true);}bin.setBlockDataMode(false);}byte tc;while ((tc = bin.peekByte()) == TC_RESET) {bin.readByte();handleReset();}depth++;totalObjectRefs++;try {switch (tc) {case TC_NULL:return readNull();case TC_REFERENCE:return readHandle(unshared);case TC_CLASS:return readClass(unshared);case TC_CLASSDESC:case TC_PROXYCLASSDESC:return readClassDesc(unshared);case TC_STRING:case TC_LONGSTRING:return checkResolve(readString(unshared));case TC_ARRAY:return checkResolve(readArray(unshared));case TC_ENUM:return checkResolve(readEnum(unshared));case TC_OBJECT:return checkResolve(readOrdinaryObject(unshared));case TC_EXCEPTION:IOException ex = readFatalException();throw new WriteAbortedException("writing aborted", ex);case TC_BLOCKDATA:case TC_BLOCKDATALONG:if (oldMode) {bin.setBlockDataMode(true);bin.peek(); // force header readthrow new OptionalDataException(bin.currentBlockRemaining());} else {throw new StreamCorruptedException("unexpected block data");}case TC_ENDBLOCKDATA:if (oldMode) {throw new OptionalDataException(true);} else {throw new StreamCorruptedException("unexpected end of block data");}default:throw new StreamCorruptedException(String.format("invalid type code: %02X", tc));}} finally {depth--;bin.setBlockDataMode(oldMode);}}現在我們來看一下readEnum,進來private Enum<?> readEnum(boolean unshared),這個方法上面都是各種校驗,觀察重點的東西,看一下String name = readString(false);行,通過readString這個方法呢,獲取枚舉對象的名稱name,然后看一下行,Enum<?> result = null;Enum聲明成null,Enum<?> en = Enum.valueOf((Class)cl, name);這一行進行賦值,再通過這個類型和name來獲取枚舉常量,那因為枚舉中的name唯一的,并且對應一個枚舉常量,所以這里拿到的肯定是唯一的枚舉常量,這樣就沒有創建新的對象,維持了這個對象的單例屬性,那ObjectInputStream里邊,關于枚舉處理的一個方法,還是比較簡單的,而且非常容易理解,所以枚舉類對于序列號這種破壞,是不受影響的,那我們接著回來,Test里面,那枚舉類不受影響,直接看一下反射呢
package com.learn.design.pattern.creational.singleton;import java.io.IOException;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;public class Test {public static void main(String[] args) throws IOException, ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
// LazySingleton lazySingleton = LazySingleton.getInstance();// System.out.println("main thread"+ThreadLocalInstance.getInstance());
// System.out.println("main thread"+ThreadLocalInstance.getInstance());
// System.out.println("main thread"+ThreadLocalInstance.getInstance());
// System.out.println("main thread"+ThreadLocalInstance.getInstance());
// System.out.println("main thread"+ThreadLocalInstance.getInstance());
// System.out.println("main thread"+ThreadLocalInstance.getInstance());// Thread t1 = new Thread(new T());
// Thread t2 = new Thread(new T());
// t1.start();
// t2.start();
// System.out.println("program end");// HungrySingleton instance = HungrySingleton.getInstance();
// EnumInstance instance = EnumInstance.getInstance();
// instance.setData(new Object());// ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("singleton_file"));
// oos.writeObject(instance);// File file = new File("singleton_file");
// ObjectInputStream ois = new ObjectInputStream(new FileInputStream(file));// HungrySingleton newInstance = (HungrySingleton) ois.readObject();
// EnumInstance newInstance = (EnumInstance) ois.readObject();// System.out.println(instance.getData());
// System.out.println(newInstance.getData());
// System.out.println(instance.getData() == newInstance.getData());// Class objectClass = HungrySingleton.class;
// Class objectClass = StaticInnerClassSingleton.class;// Class objectClass = LazySingleton.class;Class objectClass = EnumInstance.class;/*** 現在已經報異常了* 報的是NoSuchException* 那這個異常就比較有意思了* 那這個異常就出現在objectClass.getDeclaredConstructor();* 就是獲取構造器的時候呢* 并沒有獲得無參構造器* 那為什么沒有獲得無參構造器呢* 那這個困惑我們現在就來解決* 看一下java.lang.Enum* * */Constructor constructor = objectClass.getDeclaredConstructor();
// Constructor constructor = objectClass.getDeclaredConstructor(String.class,int.class);
//constructor.setAccessible(true);EnumInstance instance = (EnumInstance) constructor.newInstance();
// EnumInstance instance = (EnumInstance) constructor.newInstance("Geely",666);//
// LazySingleton newInstance = (LazySingleton) constructor.newInstance();
// LazySingleton instance = LazySingleton.getInstance();// StaticInnerClassSingleton instance = StaticInnerClassSingleton.getInstance();
// StaticInnerClassSingleton newInstance = (StaticInnerClassSingleton) constructor.newInstance();// HungrySingleton newInstance = (HungrySingleton) constructor.newInstance();
// HungrySingleton instance = HungrySingleton.getInstance();// System.out.println(instance);
// System.out.println(newInstance);// System.out.println(instance == newInstance);// EnumInstance instance = EnumInstance.getInstance();
// instance.printTest();}
}
枚舉類的源碼最大化一下public abstract class Enum<E extends Enum<E>>implements Comparable<E>, Serializable 我們看一下這個類是抽象類,往下看,這些都是聲明,注意看這里/*** Sole constructor. Programmers cannot invoke this constructor.* It is for use by code emitted by the compiler in response to* enum type declarations.** @param name - The name of this enum constant, which is the identifier* used to declare it.* @param ordinal - The ordinal of this enumeration constant (its position* in the enum declaration, where the initial constant is assigned* an ordinal of zero).*/
protected Enum(String name, int ordinal) {this.name = name;this.ordinal = ordinal;
}這個方法就是Enum的構造方法,我們再看一下還有別的方法嗎,已經沒有了,也就是說Enum這個類呢,只有一個構造器,并且這個不是無參的,而是需要傳兩個參數,才能構造出來,那我們就在Test里面不調用他的無參構造器,我們直接按照構造器這個類型直接傳進去,一個String,一個IntConstructor constructor = objectClass.getDeclaredConstructor(String.class,int.class);
package com.learn.design.pattern.creational.singleton;import java.io.IOException;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;public class Test {public static void main(String[] args) throws IOException, ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
// LazySingleton lazySingleton = LazySingleton.getInstance();// System.out.println("main thread"+ThreadLocalInstance.getInstance());
// System.out.println("main thread"+ThreadLocalInstance.getInstance());
// System.out.println("main thread"+ThreadLocalInstance.getInstance());
// System.out.println("main thread"+ThreadLocalInstance.getInstance());
// System.out.println("main thread"+ThreadLocalInstance.getInstance());
// System.out.println("main thread"+ThreadLocalInstance.getInstance());// Thread t1 = new Thread(new T());
// Thread t2 = new Thread(new T());
// t1.start();
// t2.start();
// System.out.println("program end");// HungrySingleton instance = HungrySingleton.getInstance();
// EnumInstance instance = EnumInstance.getInstance();
// instance.setData(new Object());// ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("singleton_file"));
// oos.writeObject(instance);// File file = new File("singleton_file");
// ObjectInputStream ois = new ObjectInputStream(new FileInputStream(file));// HungrySingleton newInstance = (HungrySingleton) ois.readObject();
// EnumInstance newInstance = (EnumInstance) ois.readObject();// System.out.println(instance.getData());
// System.out.println(newInstance.getData());
// System.out.println(instance.getData() == newInstance.getData());// Class objectClass = HungrySingleton.class;
// Class objectClass = StaticInnerClassSingleton.class;// Class objectClass = LazySingleton.class;Class objectClass = EnumInstance.class;// Constructor constructor = objectClass.getDeclaredConstructor();/*** 那這一行肯定OK了* 我們再run一下* 先不進行對比* 先run起來* 看一下行不行* 果然這一行就過了* * */Constructor constructor = objectClass.getDeclaredConstructor(String.class,int.class);
//constructor.setAccessible(true);
// EnumInstance instance = (EnumInstance) constructor.newInstance();/*** 這個時候我們就可以調用構造器的newInstance方法* 然后第一個傳String"Geely"* 第二個數字666* 然后前面用EnumInstance接收一下* 強轉* * 在這一行的時候出現了異常* 而這個異常看一下* IllegalArgumentException* 非法的參數異常* 后面還有說明* Cannot reflectively create enum objects* 不能反射去創建對象* 這個說明說的非常明顯* 鼠標放到這* at java.lang.reflect.Constructor.newInstance(Constructor.java:417)* 構造器的417行* newInstance這個方法里邊* 拋出來的* 那我們就進newInstance里邊看一下* * * */EnumInstance instance = (EnumInstance) constructor.newInstance("Geely",666);//
// LazySingleton newInstance = (LazySingleton) constructor.newInstance();
// LazySingleton instance = LazySingleton.getInstance();// StaticInnerClassSingleton instance = StaticInnerClassSingleton.getInstance();
// StaticInnerClassSingleton newInstance = (StaticInnerClassSingleton) constructor.newInstance();// HungrySingleton newInstance = (HungrySingleton) constructor.newInstance();
// HungrySingleton instance = HungrySingleton.getInstance();// System.out.println(instance);
// System.out.println(newInstance);// System.out.println(instance == newInstance);// EnumInstance instance = EnumInstance.getInstance();
// instance.printTest();}
}
剛剛有提示417行@CallerSensitivepublic 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 volatileif (ca == null) {ca = acquireConstructorAccessor();}@SuppressWarnings("unchecked")T inst = (T) ca.newInstance(initargs);return inst;}找到417行,throw new IllegalArgumentException("Cannot reflectively create enum objects");if ((clazz.getModifiers() & Modifier.ENUM) != 0)這里就是判斷他是不是枚舉類型,看一下我們要使用newInstance這個目標類,是不是枚舉類型,所以對于枚舉類型,通過反射,失敗了,無法進行反射攻擊,接著回來,目前來看枚舉還是非常強大的,那剛才說的序列號相關的,反射相關的,都是其他類來處理的,也就是說ObjectInptuStream,還有構造器這個類,他們兩處理枚舉類的邏輯,那枚舉類本身又什么優勢呢,通過EnumInstance,我們看不出來什么,那沒有關系,現在分享一個非常有用的反編譯工具,JAD,網址就是這個網址: https://varaneckas.com/jad/
這個網址: https://varaneckas.com/jad/,可以登陸這個網站,來下載JAD,這里面有不同系統的版本,linux,windows,mac都可以下載到,那下載完成之后呢,下載完之后呢是一個解壓包,只要解壓就可以使用了,那來到命令行,一起來操作一下,我已經下載好了,一起來看一下,為了方便我配置了環境變量,把jad放到了path里邊,我們看一下編譯出來的目錄會在哪里,這里有一個target,我們找到對應的目錄,C:\JavaEE_Workspace\demo\target\classes\com\learn\design\pattern\creational\singleton這里有一個EnumInstance.class,用JAD來反編譯這個class,直接右鍵copy path,jad C:\JavaEE_Workspace\demo\target\classes\com\learn\design\pattern\creational\singleton\EnumInstance.class非常簡單,這個命令還有一些參數,可以上網查一下,或者看官方的幫助文檔,我們就用最簡單的命令來反編譯他,我們可以看到反編譯已經完成了
生成了EnumInstance.jad,我們直接進來看一下,因為字體比較大,// Decompiled by Jad v1.5.8g. Copyright 2001 Pavel Kouznetsov.
// Jad home page: http://www.kpdus.com/jad.html
// Decompiler options: packimports(3)
// Source File Name: EnumInstance.javapackage com.learn.design.pattern.creational.singleton;public final class EnumInstance extends Enum
{private EnumInstance(String s, int i){super(s, i);}public Object getData(){return data;}public void setData(Object data){this.data = data;}public static EnumInstance getInstance(){return INSTANCE;}public static EnumInstance[] values(){EnumInstance aenuminstance[];int i;EnumInstance aenuminstance1[];System.arraycopy(aenuminstance = ENUM$VALUES, 0, aenuminstance1 = new EnumInstance[i = aenuminstance.length], 0, i);return aenuminstance1;}public static EnumInstance valueOf(String s){return (EnumInstance)Enum.valueOf(com/learn/design/pattern/creational/singleton/EnumInstance, s);}public static final EnumInstance INSTANCE;private Object data;private static final EnumInstance ENUM$VALUES[];static {INSTANCE = new EnumInstance("INSTANCE", 0);ENUM$VALUES = (new EnumInstance[] {INSTANCE});}
}我們一點點看吧,首先我們看一下這個類是final的,這個比較有意思,我們在代碼里面是根本就看不出來的,那繼續往下看,private EnumInstance(String s, int i){super(s, i);}這個是比較符合單例模式的要求的,私有構造器,接著往下看,public Object getData(){return data;}public void setData(Object data){this.data = data;}這個是data的get/set方法,繼續,public static EnumInstance getInstance(){return INSTANCE;}這個就是我們寫的getInstance方法,繼續往下看,public static final EnumInstance INSTANCE;我們可以看到這個INSTANCE對象,是一個static,并且呢,final的,那么他在什么時候實例化呢,往下看,static {INSTANCE = new EnumInstance("INSTANCE", 0);ENUM$VALUES = (new EnumInstance[] {INSTANCE});}它是通過靜態塊的方式,來實例化他,所以本身Enum這個類啊,他在加載的時候,就把INSTANCE這個對象聲明好了,這里聲明這個單例對象呢,和餓漢模式非常像,所以呢通過這些點,本身ENUM這個類,是非常符合單例模式的要求的,那我們從頭再看一遍,final代表這個,構造器私有這里private EnumInstance(String s, int i){super(s, i);}不允許外部進行實例化,同時類變量是靜態的,INSTANCE是靜態的,沒有延遲初始化,通過這個靜態塊在類加載的時候,把這個對象初始化完成,所以呢他也是線程安全的,那除了這些之外,還有IO相關的類,反射相關的類,來為枚舉類保駕護航,適當的拋出異常,所以枚舉類實現單例模式還是比較優雅的,那關于JAD這個工具,有興趣的可以多使用他,通過它可以發現很多有意思的地方,在這里也推薦大家多使用JAD反編譯工具,可能能幫你發現一些新大陸,對于某些代碼反編譯出來,很容易豁然開朗,那接著回到代碼里邊,可能枚舉類平時使用的不多,項目中使用枚舉類也是使用的比較簡單的方式,那這個類里面本身還可以聲明方法,那我們現在來領著大家來實戰一下
package com.learn.design.pattern.creational.singleton;public enum EnumInstance {/*** 例如我們在這里加一個大括號* 我們就聲明一個protected方法* * */INSTANCE{protected void printTest(){/*** 直接輸出* 那還可以繼續聲明* * */System.out.println("Geely Print Test");}};/*** 為了外部能夠訪問到printTest方法呢* 我們還要聲明一個方法* 抽象方法* 方法名和這個保持一致* 就可以了* 但是如果沒有這個聲明* 外部是訪問不了這個方法的* 你們也可以試一下* 講到這里* 希望你們通過JAD反編譯工具* 把現在寫的類再反編譯一次* 嘗試一下自己去看printTest方法到底是怎么回事* 那也算留一個小作業了* 相信通過這個過程* 也能提高很多* 至少JAD工具會用* 并且也能看懂* 為什么現在枚舉類寫方法的時候* 我們要這么寫* 我們來到Test里邊* * * */protected abstract void printTest();private Object data;public Object getData() {return data;}public void setData(Object data) {this.data = data;}/*** 獲取這個枚舉類型* * @return*/public static EnumInstance getInstance(){return INSTANCE;}}
package com.learn.design.pattern.creational.singleton;import java.io.IOException;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;public class Test {public static void main(String[] args) throws IOException, ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
// LazySingleton lazySingleton = LazySingleton.getInstance();// System.out.println("main thread"+ThreadLocalInstance.getInstance());
// System.out.println("main thread"+ThreadLocalInstance.getInstance());
// System.out.println("main thread"+ThreadLocalInstance.getInstance());
// System.out.println("main thread"+ThreadLocalInstance.getInstance());
// System.out.println("main thread"+ThreadLocalInstance.getInstance());
// System.out.println("main thread"+ThreadLocalInstance.getInstance());// Thread t1 = new Thread(new T());
// Thread t2 = new Thread(new T());
// t1.start();
// t2.start();
// System.out.println("program end");// HungrySingleton instance = HungrySingleton.getInstance();
// EnumInstance instance = EnumInstance.getInstance();
// instance.setData(new Object());// ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("singleton_file"));
// oos.writeObject(instance);// File file = new File("singleton_file");
// ObjectInputStream ois = new ObjectInputStream(new FileInputStream(file));// HungrySingleton newInstance = (HungrySingleton) ois.readObject();
// EnumInstance newInstance = (EnumInstance) ois.readObject();// System.out.println(instance.getData());
// System.out.println(newInstance.getData());
// System.out.println(instance.getData() == newInstance.getData());// Class objectClass = HungrySingleton.class;
// Class objectClass = StaticInnerClassSingleton.class;// Class objectClass = LazySingleton.class;
// Class objectClass = EnumInstance.class;// Constructor constructor = objectClass.getDeclaredConstructor();/*** 那這一行肯定OK了* 我們再run一下* 先不進行對比* 先run起來* 看一下行不行* 果然這一行就過了* * */
// Constructor constructor = objectClass.getDeclaredConstructor(String.class,int.class);
//
// constructor.setAccessible(true);
// EnumInstance instance = (EnumInstance) constructor.newInstance();/*** 這個時候我們就可以調用構造器的newInstance方法* 然后第一個傳String"Geely"* 第二個數字666* 然后前面用EnumInstance接收一下* 強轉* * 在這一行的時候出現了異常* 而這個異常看一下* IllegalArgumentException* 非法的參數異常* 后面還有說明* Cannot reflectively create enum objects* 不能反射去創建對象* 這個說明說的非常明顯* 鼠標放到這* at java.lang.reflect.Constructor.newInstance(Constructor.java:417)* 構造器的417行* newInstance這個方法里邊* 拋出來的* 那我們就進newInstance里邊看一下* * * */
// EnumInstance instance = (EnumInstance) constructor.newInstance("Geely",666);//
// LazySingleton newInstance = (LazySingleton) constructor.newInstance();
// LazySingleton instance = LazySingleton.getInstance();// StaticInnerClassSingleton instance = StaticInnerClassSingleton.getInstance();
// StaticInnerClassSingleton newInstance = (StaticInnerClassSingleton) constructor.newInstance();// HungrySingleton newInstance = (HungrySingleton) constructor.newInstance();
// HungrySingleton instance = HungrySingleton.getInstance();// System.out.println(instance);
// System.out.println(newInstance);// System.out.println(instance == newInstance);EnumInstance instance = EnumInstance.getInstance();/*** 結果已經出來了* 這個方法已經執行了* 那我們通過學習枚舉類呢* 你一定能夠提高很多* 那關于我們單例模式* 這里重點說一下* 單例模式實現的方案很多* 每一種都有自己的優缺點* 所以我們要根據不同的業務場景來選擇自己的單例模式* 還有這種模式是如何演進的* 這幾種單例方式都解決了什么問題* 他又存在什么問題* 如果問設計模式的話* 百分之九十九都會問單例這個模式* 單例模式看起來非常的簡單* 對于初學者來說他也是非常簡單的* 但是中間就是隔著一層窗戶紙* 只要一捅破* 單例模式是這些模式中最復雜的模式* 如果要深入研究的話* 這里面的知識點還是非常有意思的* 相信通過學習單例模式呢* 對于提高個人的思維能力* 編碼能力* 實操能力* 這些肯定會有所提高的* 另外希望有一點* 如果學了這門課程* 然后單例模式也都聽完了* * * */instance.printTest();}
}
?
總結
以上是生活随笔為你收集整理的单例设计模式-Enum枚举单例、原理源码解析以及反编译实战的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 单例设计模式-反射攻击解决方案及原理分析
- 下一篇: 单例模式源码分析(jdk+spring+