一、定义
确保某一个类只有一个实例,并且自行实例化并向整个系统提供这个实例。
二、使用场景
确保某个类有且只有一个对象的场景,避免产生多个对象消耗过多的资源,或者某种类型的对象只应该有且只有一个。例如,创建一个对象需要消耗的资源过多,如要访问IO和数据库等资源,这时就应考虑单例模式。
三、实现关键
1.构造函数不对外开放,一般为Private;
2.通过一个静态方法或者枚举返回单例类对象;
3.确保单例类的对象有且只有一个,尤其是在多线程环境下;
4.确保单例类对象在反序列化时不会重新构建对象。
四、代码实现
(1)懒汉模式
1.优点:只有使用时才被实例化,在一定程度上节约了资源;
2.缺点:第一次加载需及时进行实例化,反应稍慢;每次都调用getInstance造成不必要的同步开销
public class Singleton {
//懒汉模式
private static Singleton instance;
private Singleton() {
}
public static synchronized Singleton getInstance() {
if (instance == null) {
instance = new Singleton();
}
return instance;
}
}
(2)DCL方式(不适用于并发场景比较复杂或者低于JDK6版本)
1.优点:资源利用率高,第一次执行getInstance时单例对象才被实例化,效率高。
2.缺点:第一次加载反应稍慢;也由于java内存模型原因偶尔失败,在高并发也有一定缺陷,虽然发生概率很小。
public class Singleton {
//Double Check Lock(DCL)方式
private static Singleton singleton = null;
private Singleton() {
}
public static Singleton getSingleton() {
if (singleton == null) {
synchronized (Singleton.class) {
if (singleton == null) {
singleton = new Singleton();
}
}
}
}
}
(3)静态内部类(推荐使用)
1.优点:确保线程安全,保证单例对象的唯一性,同时也延迟了单例的实例化
public class Singleton {
//静态内部类单例模式
private Singleton() {
}
public static Singleton getInstance() {
return SingletonHolder.sInstance;
}
/*
* 静态内部类
*/
private static class SingletonHolder {
private static final Singleton sInstance = new Singleton();
}
}
------上述几个示例如果要杜绝单例对象在被反序列化时重新生成对象,那么必须加入如下方法:-------
private Object readResolve() throws ObjectStreamException{
return sInstance;
}
也就是在readResolve方法中将sInstance对象返回,而不是默认重新生成一个新对象。
而对于枚举则并不存在这问题。
(4)枚举
1.优点:线程安全,任何情况下都是单例;
//枚举单例
public enum SingletonEnum {
INSTANCE;
public void doSomething() {
System.out.print("do sth.");
}
}
(5)使用容器
1.优点:降低用户使用成本,降低耦合度;
//使用容器实现单例模式
public class SingletonManager {
private static Map<String, Object> objMap = new HashMap<String, Object>();
private SingletonManager() {
}
public static void registerService(String key, Object instance) {
if (!objMap.containsKey(key)) {
objMap.put(key, instance);
}
}
public static Object getService(String key) {
return objMap.get(key);
}
}
五、总结:
1.优点:
(1)在内存中只有一个实例,减少了内存开支
(2)减少系统的性能开销,如读取配置、产生其他依赖对象
(3)避免对资源的多重占用,例如一个写文件操作
(4)可以在系统设置全局的访问点,优化和共享资源访问,例如可以设计一个单例类,负责所有数据表的映射处理
2.缺点:
(1)单例模式一般没有接口,不便于扩展;
(2)单例对象如果持有Context,那么容易引起内存泄漏,此时需注意传递给单例对象的Context最好是Application Context.