一直都在想如何在Java寫一個抽象類,讓該抽象類的全部子類都限定爲單例模式,一個類須要設計成單例時直接繼承該抽象類,而單例的限定與實例獲取交給抽象類來完成。一個傳統的單例類形如一下形式:java
1 public class Singleton { 2 private static final Singleton singleton = new Singleton(); 3 4 //限制產生多個對象 5 private Singleton(){ 6 } 7 8 //經過該方法得到實例對象 9 public static Singleton getInstance(){ 10 return singleton; 11 } 12 13 //類中的其餘方法 14 public void doSomething(){ 15 } 16 17 }
根據單例實例構造的時機和方式不一樣,單例模式還能夠分紅幾種,但對於這種經過私有化構造函數,靜態方法提供實例的單例類而言,是不支持繼承的。這種模式的單例實現要求每一個具體的單例類自身來維護單例實例和限制多個實例的生成。能夠採用另一種實現單例的思路:登記式單例,來使得單例對繼承開放。app
1 import java.lang.reflect.Constructor; 2 import java.lang.reflect.InvocationTargetException; 3 import java.util.HashMap; 4 import java.util.Map; 5 6 public abstract class AbstractSingleton { 7 private static Map<String, AbstractSingleton> registryMap = new HashMap<String, AbstractSingleton>(); 8 9 AbstractSingleton() throws SingletonException{ 10 String clazzName = this.getClass().getName(); 11 if (registryMap.containsKey(clazzName)){ 12 throw new SingletonException("Cannot construct instance for class " + clazzName + ", since an instance already exists!"); 13 } else { 14 synchronized(registryMap){ 15 if (registryMap.containsKey(clazzName)){ 16 throw new SingletonException("Cannot construct instance for class " + clazzName + ", since an instance already exists!"); 17 } else { 18 registryMap.put(clazzName, this); 19 } 20 } 21 } 22 } 23 24 @SuppressWarnings("unchecked") 25 public static <T extends AbstractSingleton> T getInstance(final Class<T> clazz) throws InstantiationException, IllegalAccessException{ 26 String clazzName = clazz.getName(); 27 if(!registryMap.containsKey(clazzName)){ 28 synchronized(registryMap){ 29 if(!registryMap.containsKey(clazzName)){ 30 T instance = clazz.newInstance(); 32 return instance; 33 } 34 } 35 } 36 return (T) registryMap.get(clazzName); 37 } 38 39 public static AbstractSingleton getInstance(final String clazzName) 40 throws ClassNotFoundException, InstantiationException, IllegalAccessException{ 41 if(!registryMap.containsKey(clazzName)){ 42 Class<? extends AbstractSingleton> clazz = Class.forName(clazzName).asSubclass(AbstractSingleton.class); 43 synchronized(registryMap){ 44 if(!registryMap.containsKey(clazzName)){ 45 AbstractSingleton instance = clazz.newInstance(); 47 return instance; 48 } 49 } 50 } 51 return registryMap.get(clazzName); 52 } 53 54 @SuppressWarnings("unchecked") 55 public static <T extends AbstractSingleton> T getInstance(final Class<T> clazz, Class<?>[] parameterTypes, Object[] initargs) 56 throws SecurityException, NoSuchMethodException, IllegalArgumentException, 57 InvocationTargetException, InstantiationException, IllegalAccessException{ 58 String clazzName = clazz.getName(); 59 if(!registryMap.containsKey(clazzName)){ 60 synchronized(registryMap){ 61 if(!registryMap.containsKey(clazzName)){ 62 Constructor<T> constructor = clazz.getConstructor(parameterTypes); 63 T instance = constructor.newInstance(initargs); 65 return instance; 66 } 67 } 68 } 69 return (T) registryMap.get(clazzName); 70 } 71 72 static class SingletonException extends Exception { 73 /** 74 * 75 */ 76 private static final long serialVersionUID = -8633183690442262445L; 77 78 private SingletonException(String message){ 79 super(message); 80 } 81 } 82 83 }
以上代碼實現了一個抽象類,該類使用一個靜態的Map來維護各個子類的實例。在構造方法中判斷對應子類的實例是否已登記,若已登記則拋出一個SingletonException阻止實例的建立。因爲構造子類的實例必須先執行父類的構造方法,所以子類第一次經過構造方法建立對象時,父類構造方法會自動把實例登記,之後再調用該子類的構造方法則會拋出異常,即使子類構造方法是public的,也只能成功建立一個實例。同時父類提供幾個getInstance方法,經過傳入須要獲取實例的子類Class對象或Class限定名來獲取對應的實例。前兩個方法須要子類提供無參構造方法,第三個getInstance方法提供子類只有有參構造方法的狀況下,經過參數構造子類對象。下面給出兩個具體實現類的例子:函數
1 public class ConcreteSingleton1 extends AbstractSingleton { 2 3 public ConcreteSingleton1() throws SingletonException { 4 super(); 5 // TODO Auto-generated constructor stub 6 } 7 8 public static ConcreteSingleton1 getInstance(){ 9 try { 10 return AbstractSingleton.getInstance(ConcreteSingleton1.class); 11 } catch (InstantiationException e) { 12 // TODO Auto-generated catch block 13 // will not happen 14 e.printStackTrace(); 15 return null; 16 } catch (IllegalAccessException e) { 17 // TODO Auto-generated catch block 18 // will not happen 19 e.printStackTrace(); 20 return null; 21 } 22 } 23 24 } 25 26 public class ConcreteSingleton2 extends AbstractSingleton { 27 private final int value; 28 29 public ConcreteSingleton2(int value) throws SingletonException { 30 super(); 31 // TODO Auto-generated constructor stub 32 this.value = value; 33 } 34 35 public static ConcreteSingleton2 getInstance(int value){ 36 try { 37 return AbstractSingleton.getInstance(ConcreteSingleton2.class, new Class<?>[]{int.class}, new Object[]{value}); 38 } catch (Exception e) { 39 // TODO Auto-generated catch block 40 e.printStackTrace(); 41 return null; 42 } 43 } 44 45 public int getValue(){ 46 return value; 47 } 48 49 }
兩個實現類都繼承自AbstractSingleton。其中ConcreteSingleton1構造方法無參,ConcreteSingleton2有一個int型參數。兩個類都各自提供了一個getInstance方法簡化實例的獲取。兩個類的構造方法都不是私有的,外界能夠經過new來視圖屢次建立對象,但只有該類對象沒有建立過任何一個實例時,new才能成功得到對象,不然會拋出異常。兩個類中的getInstance方法徹底只是爲了簡化獲取單例對象的方式,能夠不寫直接經過AbstractSingleton中的相應方法獲取對象。除此以外,兩個類都和正常java類無異,單例的維護和限定完全交給抽象類來完成,只須要繼承AbstractSingleton便可實現單例。測試
測試代碼以下:this
1 public class TestSingleton { 2 3 /** 4 * @param args 5 */ 6 public static void main(String[] args) { 7 // TODO Auto-generated method stub 8 ConcreteSingleton1 singleton1 = null; 9 ConcreteSingleton2 singleton2 = null; 10 11 // 第一次建立實例,建立成功 12 try { 13 singleton1 = new ConcreteSingleton1(); 14 System.out.println(singleton1); 15 } catch (SingletonException e) { 16 // TODO Auto-generated catch block 17 e.printStackTrace(); 18 } 19 20 try { 21 singleton2 = new ConcreteSingleton2(5); 22 System.out.println(singleton2 + "---" + singleton2.getValue()); 23 } catch (SingletonException e) { 24 // TODO Auto-generated catch block 25 e.printStackTrace(); 26 } 27 28 29 // 獲取單例實例,與前面new出來的實例是同一個實例 30 singleton1 = ConcreteSingleton1.getInstance(); 31 System.out.println(singleton1); 32 33 singleton2 = ConcreteSingleton2.getInstance(6); 34 System.out.println(singleton2 + "---" + singleton2.getValue()); 35 36 // 試圖建立第二個實例,拋出異常,建立失敗 37 try { 38 singleton1 = new ConcreteSingleton1(); 39 System.out.println(singleton1); 40 } catch (SingletonException e) { 41 // TODO Auto-generated catch block 42 e.printStackTrace(); 43 } 44 45 try { 46 singleton2 = new ConcreteSingleton2(5); 47 System.out.println(singleton2 + "---" + singleton2.getValue()); 48 } catch (SingletonException e) { 49 // TODO Auto-generated catch block 50 e.printStackTrace(); 51 } 52 53 } 54 55 }
運行結果以下:spa
ConcreteSingleton1@c3c749 ConcreteSingleton2@1bc4459---5 ConcreteSingleton1@c3c749 ConcreteSingleton2@1bc4459---5 AbstractSingleton$SingletonException: Cannot construct instance for class ConcreteSingleton1, since an instance already exists! at AbstractSingleton.<init>(AbstractSingleton.java:14) at ConcreteSingleton1.<init>(ConcreteSingleton1.java:6) at TestSingleton.main(TestSingleton.java:42) AbstractSingleton$SingletonException: Cannot construct instance for class ConcreteSingleton2, since an instance already exists! at AbstractSingleton.<init>(AbstractSingleton.java:14) at ConcreteSingleton2.<init>(ConcreteSingleton2.java:7) at TestSingleton.main(TestSingleton.java:50)
雖然採用getInstance方法獲取ConcreteSingleton2的對象時傳入的參數是6,可是因爲以前已經實例化過對象,直接得到以前登記的對象,此時輸出的value仍然是5。設計
接下來把第一次使用new建立實例的代碼註釋掉,經過getInstance方法經過反射建立實例,這一次ConcreteSingleton2對象輸出的value應該是6,運行結果以下:code
ConcreteSingleton1@c3c749 ConcreteSingleton2@1888759---6 AbstractSingleton$SingletonException: Cannot construct instance for class ConcreteSingleton1, since an instance already exists! at AbstractSingleton.<init>(AbstractSingleton.java:14) at ConcreteSingleton1.<init>(ConcreteSingleton1.java:6) at TestSingleton.main(TestSingleton.java:43) AbstractSingleton$SingletonException: Cannot construct instance for class ConcreteSingleton2, since an instance already exists! at AbstractSingleton.<init>(AbstractSingleton.java:14) at ConcreteSingleton2.<init>(ConcreteSingleton2.java:7) at TestSingleton.main(TestSingleton.java:51)