单例模式
GoF定义:保证一个类只有一个对象,并且提供一个全局的访问点
概念
一个特别的类只有一个实例,无论什么时候使用都是这同一个实例
例子
现实生活:假设有两个板球队伍在比赛,赛前两队的队长要投币决定哪方先击球,此时每个队的队长只能有一个,如果原来没有,那么要新选出一个队长
代码世界:代码只在一个文件系统上操作,那么这个文件系统的对象应该是全局唯一的
展示
- 将构造器声明为private
- 当创建这个类的实例时,如果已经存在,则使用这个实例,否则新建一个实例
public class SingletonPattern {
public static void main(String[] args) {
System.out.println("***Singleton Pattern Demo***\n");
System.out.println("Trying to make a captain for our team");
MakeACaptain c1 = MakeACaptain.getCaptain();
System.out.println("Trying to make another captain for our team");
MakeACaptain c2 = MakeACaptain.getCaptain();
if (c1 == c2)
{
System.out.println("c1 and c2 are same instance");
}
}
}
class MakeACaptain {
private static MakeACaptain makeACaptain;
private MakeACaptain() {}
public static MakeACaptain getCaptain() {
if (makeACaptain == null) {
makeACaptain = new MakeACaptain();
System.out.println("New Captain selected for our team");
} else {
System.out.print("You already have a Captain for your team.");
System.out.println("Send him for the toss.");
}
return makeACaptain;
}
}
附加信息
上面代码的初始化模式为lazy initialization
。因为直到静态方法被调用这个对象都不存在(可以在静态变量默认值直接新建一个对象)
上面代码的实例创建方式是线程不安全的,即(判断对象是否为空可能会让两个线程进入,那么会创建两个对象,那么它们持有的就不是同一个对象)
改进方式:
- 将返回实例的方法设为
synchronized
(同步操作会有额外开销(锁的开销))
public static synchronized MakeACaptain getCaptain()
- 提前把实例创建好
class MakeACaptain {
private static MakeACaptain makeACaptain = new MakeACaptain();
private MakeACaptain() {}
public static synchronized MakeACaptain getCaptain() {
return makeACaptain
}
}
- 不使用同步,也不使用提前初始化,Java中标准的单例实现(这个方法的牛逼之处在于,类在加载时会有静态变量初始化的过程,这种方法利用了内部类的静态成员变量初始化需要加载内部类,实现了
lazy initialization
。对于final变量,只有当构造器调用完成,其它的线程才能看到,这个(Java语言的)机制保证了线程安全)
class MakeACaptain
{
private static MakeACaptain _captain;
private MakeACaptain() { }
//Bill Pugh solution
private static class SingletonHelper{
//Nested class is referenced after getCaptain() is called
private static final MakeACaptain _captain = new MakeACaptain();
}
public static MakeACaptain getCaptain()
{
return SingletonHelper._captain;
}
}