了解设计模式 之 创建模式(四) -- 单例模式

 4. 单例模式(Singleton Pattern)

单例模式顾名思义,就是实现了单例,保证了在一个应用中只存在某个类的唯一实例。

单例模式包括3个变种:饿汉式,懒汉式和登记式。下边来一一的说明

(1) 饿汉式单例

Java代码 复制代码  收藏代码
  1. public class EagerSingleton {   
  2.        
  3.     // 单例   
  4.     private static final EagerSingleton instance = new EagerSingleton();   
  5.   
  6.     private EagerSingleton() {   
  7.         // 构造函数设成private保证不能通过它去实例化   
  8.     }   
  9.   
  10.     public static EagerSingleton getInstance() {   
  11.         return instance;   
  12.     }   
  13. }  
public class EagerSingleton {
	
	// 单例
	private static final EagerSingleton instance = new EagerSingleton();

	private EagerSingleton() {
		// 构造函数设成private保证不能通过它去实例化
	}

	public static EagerSingleton getInstance() {
		return instance;
	}
}


此种方式在类被加载时就已经初始化了instance,所以称之为饿汉式。

但是,这种方式有个弱点,就是构造函数是私有的,也就是说这个类是无法继承的。

(2) 懒汉式单例

Java代码 复制代码  收藏代码
  1. public class LazySingleton {   
  2.     private static LazySingleton instance = null;   
  3.   
  4.     private LazySingleton() {   
  5.     }   
  6.   
  7.     public synchronized static LazySingleton getInstance() {   
  8.         if (instance == null) {   
  9.             instance = new LazySingleton();   
  10.         }   
  11.         return instance;   
  12.     }   
  13. }  
public class LazySingleton {
	private static LazySingleton instance = null;

	private LazySingleton() {
	}

	public synchronized static LazySingleton getInstance() {
		if (instance == null) {
			instance = new LazySingleton();
		}
		return instance;
	}
}


这个方式只在需要实例化的时候才去真正的实例化instance变量,在资源使用的效率上是比饿汉式要高的,但是它的构造函数同样是私有的,也无法继承此类。

另外,还有一个需要说明的地方,#getInstance()方法有很多人是推荐使用"双重检查锁定(DCL, double check locking)"这种方式去实现的,这样就不需要对整个方法来"synchronized"了,节省系统的开销。但是,由于Java的内存模型的原因,会导致"无序写入"问题,进而使DCL失效。但是,对于双重检查锁定,也是有补救的方法的,《Head First Design Pattern》中就提到了使用"volatile"关键字的方法,不过这个方法要在JDK1.5以上才生效,具体的代码如下:

Java代码 复制代码  收藏代码
  1. public class LazySingleton {   
  2.     // 使用"volatile"   
  3.     private static volatile LazySingleton instance = null;   
  4.   
  5.     private LazySingleton() {   
  6.     }   
  7.        
  8.     // 使用DCL   
  9.     public static LazySingleton getInstance() {   
  10.         if (instance == null) {   
  11.             synchronized (LazySingleton.class) {   
  12.                 if (instance == null) {   
  13.                     instance = new LazySingleton();   
  14.                 }   
  15.             }   
  16.   
  17.         }   
  18.         return instance;   
  19.     }   
  20. }  
public class LazySingleton {
	// 使用"volatile"
	private static volatile LazySingleton instance = null;

	private LazySingleton() {
	}
	
	// 使用DCL
	public static LazySingleton getInstance() {
		if (instance == null) {
			synchronized (LazySingleton.class) {
				if (instance == null) {
					instance = new LazySingleton();
				}
			}

		}
		return instance;
	}
}


(3) 登记式单例

登记式单例克服了饿汉式和懒汉式单例都无法继承的缺点,实现的代码如下所示:

Java代码 复制代码  收藏代码
  1. public class RegisterSingleton {   
  2.     private static Map<String, RegisterSingleton> registry = new HashMap<String, RegisterSingleton>();   
  3.     static {   
  4.         RegisterSingleton x = new RegisterSingleton();   
  5.         registry.put(x.getClass().getName(), x);   
  6.     }   
  7.   
  8.     protected RegisterSingleton() {   
  9.     }   
  10.   
  11.     public static RegisterSingleton getInstance(String name) {   
  12.         if (name == null) {   
  13.             name = RegisterSingleton.class.getName();   
  14.         }   
  15.         if (registry.get(name) == null) {   
  16.             try {   
  17.                 registry.put(name, (RegisterSingleton) Class.forName(name).newInstance());   
  18.             } catch (Exception e) {   
  19.                 System.out.println("Error happened.");   
  20.             }   
  21.         }   
  22.         return registry.get(name);   
  23.     }   
  24. }  
public class RegisterSingleton {
	private static Map<String, RegisterSingleton> registry = new HashMap<String, RegisterSingleton>();
	static {
		RegisterSingleton x = new RegisterSingleton();
		registry.put(x.getClass().getName(), x);
	}

	protected RegisterSingleton() {
	}

	public static RegisterSingleton getInstance(String name) {
		if (name == null) {
			name = RegisterSingleton.class.getName();
		}
		if (registry.get(name) == null) {
			try {
				registry.put(name, (RegisterSingleton) Class.forName(name).newInstance());
			} catch (Exception e) {
				System.out.println("Error happened.");
			}
		}
		return registry.get(name);
	}
}


这样,RegisterSingleton的子类就可以实现单例了,代码如下:

Java代码 复制代码  收藏代码
  1. public class SubRegisterSingleton extends RegisterSingleton {   
  2.     public SubRegisterSingleton() {   
  3.     }   
  4.   
  5.     public static SubRegisterSingleton getInstance() {   
  6.         return (SubRegisterSingleton) RegisterSingleton.getInstance(SubRegisterSingleton.class.getName());   
  7.     }   
  8. }  
public class SubRegisterSingleton extends RegisterSingleton {
	public SubRegisterSingleton() {
	}

	public static SubRegisterSingleton getInstance() {
		return (SubRegisterSingleton) RegisterSingleton.getInstance(SubRegisterSingleton.class.getName());
	}
}


这里,您可能会注意到,SubRegisterSingleton的构造函数是public的,因为在SubRegisterSingleton的父类RegisterSingleton需要实例化一个SubRegisterSingleton的对象,所以,这个构造函数必须是public的,但是,这里也会出现客户端直接使用这个构造函数去构造对象的问题,这个是登记式单例的一个弱点。另外,子类的单例实现是一定要父类来参与的,这个也有点浪费资源。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值