第二篇 创建对象
- 直接使用new 所带来的高耦合度。
- 如何避免耦合度?
- OOP带给我们的好处之一是封装,如果封装这些实例化的细节,即对客户对象隐藏实例化的过程,
那么我们新添加一个接口的实现或者抽象类的具体类,也不会影响客户代码了,降低了耦合度。
- 客户对象(Client)和服务对象(Service) -->可移植性和重用性
-- Chapter 3: 单例模式【Singleton】
-> 如何保证一个类在一个系统里面只有一个实例?
-- Chapter 4: 工厂方法模式【Factory method】
-> 如何实例化对象使得对象和使用者之间的耦合度降低?
-- Chapter 5: 原型模式【Prototype】
-> 如何拷贝现有的对象快速的创建一个新的复杂对象?
-- Chapter 6: 控制反转【IoC】
-> IoC和依赖注入DI Inverse of contrale
# 3.1 概述
- 一个系统里只有一个类
- 常见:缓存池,数据库连接池,线程池,一些应用服务实例等
- 在多线程的环境中,为了保证实例的唯一性实则不易
# 3.2 最简单的实例
- 该类的构造方法是私有的(外部类无法创建)
- 提供一个全局访问点
- [code]
public class Singleton {
private static Singleton instance = new Singleton(); //外界无法访问
private Singleton() { //客户对象无法创建
}
public static Singleton getInstance () { //全局访问点
return instance;
}
}
[code]
注意 1. instance没有直接暴露给外界
2. 此实现是线程安全,当多个线程同时去访问该类的getInstance方法时,不会初始化多个对象,
因为JVM在加载此类时,对于static属性的初始化只能由一个线程执行且仅一次。
3. 客户端使用简便: Singleton singleton = Singleton.getInstance();
# 3.3 进阶
## 3.3.1 迟延创建
- 只有在第一次使用该类的实例化时才去实例化。
->> 把单例的实例化个过程移至getInstance方法即可,而不是在加载类时预先创建。
- [code]
public class UnThreadSaftSingleton {
private UnThreadSaftSingleton instance;
public static UnThreadSaftSingleton getInstance () {
if (instance == null) {
instance = new UnThreadSaftSingleton();
}
return instance;
}
}
[code]
## 3.3.2 线程安全
注意: 1. 高并发环境中,线程不安全的,会产生多个指向该类的实例,并出现内存泄漏的情况。
2. 解决该问题,只需要对getInstance方法加 synchronized 关键字。
## 3.3.3 Double-Check Locking
- synchronized 性能有问题 --> 对整个getInstance方法进行同步是没有必要的
- 只要保证被实例化的那段逻辑被一个线程执行就OK了,而返回引用的那段代码则没必要同步
[code]
public class DoubleCheckSingleton {
private valotile static DoubleCheckSingleton instance = null;
public static DoubleCheckSingleton getInstance () {
if(instance == null) {
synchronized(DoubleCheckSingleton.class) { //synchronize creation block
if (instance == null) {
instance = new DoubleCheckSingleton();
}
}
}
return instance;
}
}
[code]
注意: 1. 锁住初始化代码块
2. 两次判断,故称 Double checked Locking
3. 属性instance是被volatile修饰的。 作用:线程能够自动发现volatile变量的最新值
4. 此程序只有在Java 5 及以上版本才能运行
## 3.3.4 Initialization on demand holder
- 另一种实现线程安全的单例模式
[code]
public class LazyLoadedSingleton {
private LazyLoaderSingleton () {
}
private static class LazyHolder {
private static final LazyLoadedSingleton singletonInstance =
new LazyLoadedSingleton();
}
public static LazyLoadedSingleton getInstance () {
return LazyHolder.singletonInstance;
}
}
[code]
注意:只有第一调用 getInstance 时才会加载。
## 3.3.5 Singleton的序列化
- 如果Singleton实现了Serializable接口,特别需要注意:
在默认情况下,每次反序列化总会创建一个新的实例对象,这样一个系统就会出现多个对象可供使用。
- 需要在readResolve method 里面做文章
[code]
public class SerialibleSingleton implements Serializable {
private static final long serialVersionUID = 1L;
static SerialibleSingleton instance = new SerialibleSingleton();
private SerialibleSingleton(){
}
//This method is called immediately after an object of this class deserialized.
//This method return the singleton instance.
private Object readResolve () {
return instance;
}
}
[code]
- 这样内存中始终保存一个对象
# 3.5 总结
- 基于同一个Jvm中,如何保证一个类只有一个实例
- 如果在分布式环境中,可能需要考虑如何保证在整个应用(可能分布在不同的JVM上)只有一个实例。
==思路清晰,简单易懂,又不失深度,好书==