public class InnerClassSingleton implements Serializable { //無參構造函數 private InnerClassSingleton(){}; public static final InnerClassSingleton getInstance(){ return InnerClassHelper.INSTANCE; } //內部類 private static class InnerClassHelper{ private static final InnerClassSingleton INSTANCE = new InnerClassSingleton(); } }
它的原理是利用了類加載機制。java
Class clazz = InnerClassSingleton.class; Constructor c = clazz.getDeclaredConstructor(null); c.setAccessible(true); Object o1 = c.newInstance(); Object o2 = InnerClassSingleton.getInstance();
執行這段代碼會發現o1<>o2,這就破壞了單例。
爲何呢?罪魁禍首就是以下代碼,它是反射的newInstance()的底層實現。ide
UnsafeFieldAccessorImpl.unsafe.allocateInstance(class)
咱們知道new建立對象時會被編譯成3條指令:函數
而Unsafe.allocateInstance()方法值作了第一步和第二步,即分配內存空間,返回內存地址,沒有作第三步調用構造函數。因此Unsafe.allocateInstance()方法建立的對象都是隻有初始值,沒有默認值也沒有構造函數設置的值,由於它徹底沒有使用new機制,繞過了構造函數直接操做內存建立了對象,而單例是經過私有化構造函數來保證的,這就使得單例失敗。性能
InnerClassSingleton o1 = null; InnerClassSingleton o2 = InnerClassSingleton.getInstance(); FileOutputStream fos = new FileOutputStream("InnerClassSingleton.obj"); ObjectOutputStream oos = new ObjectOutputStream(fos); oos.writeObject(o2); oos.flush(); oos.close(); FileInputStream fis = new FileInputStream("InnerClassSingleton.obj"); ObjectInputStream ois = new ObjectInputStream(fis); o1 = (InnerClassSingleton) ois.readObject(); ois.close(); System.out.println(o1); System.out.println(o2);
執行完這段代碼咱們又會發現o1<>o2,可見經過反序列化,成功破壞了單例,建立了2個對象。
那麼如何避免這種狀況發生呢?很簡單,只要在代碼中添加:this
public class InnerClassSingleton implements Serializable { ....省略重複代碼 private Object readResolve(){ return InnerClassHelper.INSTANCE; } }
這時候咱們能夠再執行一下上面反序列化的方法,會很神奇的發現o1==o2,那這是爲何呢?咱們一塊兒來看下ois.readObject()的源碼:線程
private Object readObject0(boolean unshared) throws IOException { ...省略 case TC_OBJECT: return checkResolve(readOrdinaryObject(unshared)); } ------------------------------------------------------------------- private Object readOrdinaryObject(boolean unshared){ if (bin.readByte() != TC_OBJECT) { throw new InternalError(); } ObjectStreamClass desc = readClassDesc(false); desc.checkDeserialize(); Class<?> cl = desc.forClass(); if (cl == String.class || cl == Class.class || cl == ObjectStreamClass.class) { throw new InvalidClassException("invalid class descriptor"); } Object obj; try { //重點!!! //首先isInstantiable()判斷是否能夠初始化 //若是爲true,則調用newInstance()方法建立對象,這時建立的對象是不走構造函數的,是一個新的對象 obj = desc.isInstantiable() ? desc.newInstance() : null; } catch (Exception ex) { throw (IOException) new InvalidClassException( desc.forClass().getName(), "unable to create instance").initCause(ex); } passHandle = handles.assign(unshared ? unsharedMarker : obj); ClassNotFoundException resolveEx = desc.getResolveException(); if (resolveEx != null) { handles.markException(passHandle, resolveEx); } if (desc.isExternalizable()) { readExternalData((Externalizable) obj, desc); } else { readSerialData(obj, desc); } handles.finish(passHandle); //重點!!! //hasReadResolveMethod()會去判斷,咱們的InnerClassSingleton對象中是否有readResolve()方法 if (obj != null && handles.lookupException(passHandle) == null && desc.hasReadResolveMethod()) { //若是爲true,則執行readResolve()方法,而咱們在本身的readResolve()方法中 直接retrun INSTANCE,因此仍是返回的同一個對象,保證了單例 Object rep = desc.invokeReadResolve(obj); if (unshared && rep.getClass().isArray()) { rep = cloneArray(rep); } if (rep != obj) { // Filter the replacement object if (rep != null) { if (rep.getClass().isArray()) { filterCheck(rep.getClass(), Array.getLength(rep)); } else { filterCheck(rep.getClass(), -1); } } handles.setObject(passHandle, obj = rep); } } return obj; }
最後總結一下靜態內部類寫法:code
public enum EnumSingleton { INSTANCE; private Object data; public Object getData() { return data; } public void setData(Object data) { this.data = data; } public static EnumSingleton getInstance() { return INSTANCE; } }
反編譯這段代碼,獲得:對象
static { INSTANCE = new EnumSingleton("INSTANCE",0); $VALUE = (new EnumSingleton[] { INSTANCE }); }
顯然這是一種餓漢式的寫法,用static代碼塊來保證單例(在類加載的時候就初始化了)。ip
//反射 Class clazz = EnumSingleton.class; //拿到構造函數 Constructor c = clazz.getDeclaredConstructor(String.class, int.class); c.setAccessible(true); EnumSingleton instance1 = (EnumSingleton)c.newInstance("smart", 111); ----------------------------------------------------------------------------------------- public T newInstance(Object ... initargs){ if ((clazz.getModifiers() & Modifier.ENUM) != 0) throw new IllegalArgumentException("Cannot reflectively create enum objects"); }
能夠看到,在newInstance()方法中,作了類型判斷,若是是枚舉類型,直接拋出異常。也就是說從jdk層面保證了枚舉不能被反射。內存
Java規範中規定,每個枚舉類型極其定義的枚舉變量在JVM中都是惟一的,在序列化的時候Java僅僅是將枚舉對象的name屬性輸出到結果中,反序列化的時候則是經過 java.lang.Enum 的 valueOf() 方法來根據名字查找枚舉對象。
...省略 EnumSingleton o1 = (EnumSingleton) ois.readObject(); ----------------------------------------------------------------------------------- private Object readObject0(boolean unshared) throws IOException { ...省略 case TC_ENUM: return checkResolve(readEnum(unshared)); } ------------------------------------------------------------------- private Object readEnum(boolean unshared){ ...省略 String name = readString(false); Enum<?> result = null; Class<?> cl = desc.forClass(); if (cl != null) { try { @SuppressWarnings("unchecked") //重點!!! //經過valueOf方法獲取Enum,參數爲class和name 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); } } }
因此序列化的時候只將 INSTANCE 這個名稱輸出,反序列化的時候再經過這個名稱,查找對應的枚舉類型,所以反序列化後的實例也會和以前被序列化的對象實例相同。
public class Singleton { private Singleton(){} private static final ThreadLocal<Singleton> threadLocal = new ThreadLocal<Singleton>(){ @Override protected Singleton initialValue(){ return new Singleton(); } }; public static Singleton getInstance(){ return threadLocal.get(); } }
這種寫法利用了ThreadLocal的特性,能夠保證局部單例,即在各自的線程中是單例的,可是線程與線程之間不保證單例。
package com.baomidou.dynamic.datasource.toolkit; import java.util.concurrent.LinkedBlockingDeque; public final class DynamicDataSourceContextHolder { //重點!!! private static final ThreadLocal<LinkedBlockingDeque<String>> LOOKUP_KEY_HOLDER = new ThreadLocal() { protected Object initialValue() { return new LinkedBlockingDeque(); } private DynamicDataSourceContextHolder() { } public static String getDataSourceLookupKey() { LinkedBlockingDeque<String> deque = (LinkedBlockingDeque)LOOKUP_KEY_HOLDER.get(); return deque.isEmpty() ? null : (String)deque.getFirst(); } public static void setDataSourceLookupKey(String dataSourceLookupKey) { ((LinkedBlockingDeque)LOOKUP_KEY_HOLDER.get()).addFirst(dataSourceLookupKey); } public static void clearDataSourceLookupKey() { LinkedBlockingDeque<String> deque = (LinkedBlockingDeque)LOOKUP_KEY_HOLDER.get(); if (deque.isEmpty()) { LOOKUP_KEY_HOLDER.remove(); } else { deque.pollFirst(); } } }; }
PS:initialValue()通常是用來在使用時進行重寫的,若是在沒有set的時候就調用get,會調用initialValue方法初始化內容。