Java中可以使用反射來創建類的實例,即使類的構造方法私有時,也可以創建,這樣就可能導致單例模式被破壞。比如下面非常純的單例模式:
//非常普通的單例模式
public class Singleton {
private static final Singleton instance = new Singleton();
private Singleton() {
}
public static Singleton getInstance() {
return instance;
}
}
使用下面方法破壞單例:
public class SingletonKiller {
public static void main(String[] args) throws Exception {
/* 可以使用相對路徑,同一個包中可以不用帶包路徑 */
Class s = Class.forName("com.piaohan.pattern.singleton.Singleton");
Constructor ct = s.getDeclaredConstructor();
/* true:使java不檢查訪問限制,即可以使用private方法 */
ct.setAccessible(true);
System.out.println(Singleton.getInstance());
System.out.println(ct.newInstance());
System.out.println(Singleton.getInstance() == ct.newInstance());
}
}
結果如下:
com.piaohan.pattern.singleton.Singleton@67f1fba0
com.piaohan.pattern.singleton.Singleton@3fbefab0
false
反射破壞了單例模式,但是實際開發中影響不大,項目約定好了不允許這樣用就行了,很少有人會故意這麼寫,但是可以考慮使用這種方式結合工廠模式創建單例類,比如把所有要創建的單例類構造函數設置成私有,而正常寫邏輯就行。代碼如下:
//無狀態業務邏輯
public class SingletonService {
// 私有構造函數,防止外部實例化
private SingletonService() {
}
// 業務邏輯1
public void method1() {
}
// 業務邏輯2
public void method2() {
}
}
使用下面工廠生成具體的邏輯類實例對象:
//單例工廠
public class SingletonFactory {
private static Map<Class<?>, Object> objCache = new HashMap<Class<?>, Object>();
public synchronized static Object getInstance(Class<?> clazz) throws Exception {
Object singleton = objCache.get(clazz);
if (singleton == null) {
singleton = createInstance(clazz);
objCache.put(clazz, singleton);
}
return singleton;
}
private static Object createInstance(Class<?> clazz) throws Exception {
Constructor ct = clazz.getDeclaredConstructor();
ct.setAccessible(true);
return ct.newInstance();
}
}
這樣,就不用每個類都手動寫一個單例模式了,當然,這裏只是思路,實際開發中要考慮性能,併發等問題。如果能夠使用spring,就可以替代類似這個功能了。