1:採用私有構造器來強化Singleton屬性工具
顧名思義,即咱們須要定義一個private的構造器,但要注意一點,即便咱們定義了私有的構造器,可是客戶端仍是能夠藉助AccessibleObject.setAccessible方法,經過反射來調用私有的構造器,所以,咱們須要修改構造器來抵禦這種工具,下面代碼很好闡述了這個。測試
public class Singleton { private static final Singleton INSTANCE = new Singleton(); private Singleton() { if (INSTANCE != null) { throw new UnsupportedOperationException("Instance already exist"); } } public static Singleton getInstance() { return INSTANCE; } }
然而這樣作就能夠絕對防止出現多個實例了麼?其實如今還有一種狀況下會出現多個實例,那就是在你序列化這個對象以後,在進行反序列化,這個時候,你將再次獲得一個新的對象,讓咱們看下例子,首先實現序列化接口spa
public class Singleton implements Serializable{ ... }
接着咱們來看看序列化和反序列化後的狀況code
public class SerializableTest { //序列化 private static void serializable(Singleton singleton, String filename) throws IOException { FileOutputStream fos = new FileOutputStream(filename); ObjectOutputStream oos = new ObjectOutputStream(fos); oos.writeObject(singleton); oos.flush(); } //反序列化 @SuppressWarnings("unchecked") private static <T> T deserializable(String filename) throws IOException, ClassNotFoundException { FileInputStream fis = new FileInputStream(filename); ObjectInputStream ois = new ObjectInputStream(fis); return (T) ois.readObject(); } public static void main(String[] args) throws IOException, ClassNotFoundException { //序列化到文件test.txt中 serializable(Singleton.getInstance(),"F://test.txt"); //反序列化 Singleton singleton = deserializable("F://test.txt"); //比較反序列化後獲得的singleton和Singleton.getInstance的地址是否同樣。 System.out.println(singleton); System.out.println(Singleton.getInstance()); } }
測試運行的結果以下圖所示,所以,咱們很明顯能夠看出獲得的是兩個不一樣的對象對象
那麼如何解決序列化問題呢,其實很簡單,只要咱們在Singleton中加入下面方法便可,爲何只須要加入readResolve就行了呢,由於任何一個readObject方法,無論顯示仍是默認的,它都會返回一個新建的實例,這就是爲何上面兩個實例的地址是不同的緣由了,而加入了readResolve以後,那麼在反序列化以後,新建對象上的readResolve方法會被調用,而後該方法返回的對象引用將取代新建的對象,指向新建對象的引用就不會保留下來,當即成爲垃圾回收的對象了。blog
private Object readResolve() { return INSTANCE; }
2:利用枚舉來強化Singleton(最優方案)接口
枚舉來強化方式很簡單,也不會出現上面這些狀況,利用單元素的枚舉來實現單例(Singleton),絕對防止屢次實例化,實現代碼以下:get
public enum Elvis{ INSTANCE; private String[] favoriteSongs = {"Hound Dog","Heartbreak Hotl"}; public void printFavorites(){ System.out.println(Arrays.toString(favoriteSongs )); } }
調用時只須要Elvis.INSTANCE.printFavorites();便可。it