转载地址:http://blog.csdn.net/lingyun_blog/article/details/41518145
俺有6个漂亮的老婆,她们的老公都是我,我就是我们家里的老公Singleton,她们只要说道“老公”,都是指的同一个人,那就是我(刚才做了个梦啦,哪有这么好的事)
定义
有单例模式确保某一个类只有一个实例,而且自行实例化并向整个系统提供这个实例单例模式。单例模式只应在有真正的“单一实例”的需求时才可使用。
使用场景
当类只能有一个实例而且第三方可以从一个公共的访问点访问它时。
当一个唯一的实例可以通过子类化来扩展,而且第三方需要在不更改代码的情况下就能使用一个扩展的实例时。
优缺点分析
优点:
1.实例控制
对唯一的实例做出访问控制。
允许改变实例的数目,事实上单例模式只是实例有限的类的一种特列,通过计数等方式可以做到双例模式,三例模式等需要实例化有限次的类。
2.灵活性
因为类控制了实例化过程,所以类可以灵活更改实例化过程。
缺点:
1.开销
虽然数量很少,但如果每次对象请求引用时都要检查是否存在类的实例,将仍然需要一些开销。可以通过使用静态初始化解决此问题。
2.可能的开发混淆
使用单例对象(尤其在类库中定义的对象)时,开发人员必须记住自己不能使用new关键字实例化对象。因为可能无法访问库源代码,因此应用程序开发人员可能会意外发现自己无法直接实例化此类。
3.对象生存期
不能解决删除单个对象的问题。在提供内存管理的语言中(例如基于.NET Framework的语言),只有单例类能够导致实例被取消分配,因为它包含对该实例的私有引用。在某些语言中(如 C++),其他类可以删除对象实例,但这样会导致单例类中出现悬浮引用。
单例模式UML图
应用场景及代码分析
在开头的时候提到梦中的时候梦到了自己有好多的老婆,有中国的老婆、有美国的老婆…(ps:在我们伟大的天朝里,可是施行一夫一妻、男女平等的婚姻制度)。为了给大家演示单例模式,我们假设有2个老婆,一个中国的老婆、一个美国的老婆,老公只有一个,老公要时常的陪妻子约会,妻子觉得单独和老公出去才是浪漫,所以她们都想老公只带自己出去约会。
首先我们先看一下老公的类,因为老公只有一个,所以老公我们使用单例模式创建,如下代码:
//单例,老公类
public class Husband {
//老公只有一个
private static Husband instance;
private Husband() {
}
public static Husband getInstance() {
if (instance == null) {
instance = new Husband();
}
return instance;
}
public void show(){
System.out.println("老公可是一个安静的美男子!");
}
public void date(){
System.out.println("美男子可以和亲爱的妻子约会了!");
}
}
两个老婆分别有找老公和约会的方法
//中国妻子类
public class ChinaWife {
private Husband myHusband = null;
//找老公
public void showHusband(){
myHusband = Husband.getInstance();
myHusband.show();
}
//和老公约会
public void dateWithHusband(){
myHusband = Husband.getInstance();
myHusband.date();
}
}
//美国妻子
public class AmericanWife {
private Husband myHusband = null;
//找老公
public void showHusband(){
myHusband = Husband.getInstance();
myHusband.show();
}
//和老公约会
public void dateWithHusband(){
myHusband = Husband.getInstance();
myHusband.date();
}
}
实现了老公和妻子的代码以后,我们要开始测试了:
public class SingletonTest {
public static void main(String[] args){
AmericanWife amWife = new AmericanWife();
System.out.println("美国妻子:我要找老公!");
amWife.showHusband();
System.out.println("美国妻子:老公和我约会吧!");
amWife.dateWithHusband();
ChinaWife chWife = new ChinaWife();
System.out.println("中国妻子:我要找老公!");
chWife.showHusband();
System.out.println("中国妻子:老公和我约会吧!");
chWife.dateWithHusband();
}
}
结果如下:
美国妻子:我要找老公!
老公可是一个安静的美男子!
美国妻子:老公和我约会吧!
美男子可以和亲爱的妻子约会了!
中国妻子:我要找老公!
老公可是一个安静的美男子!
中国妻子:老公和我约会吧!
美男子可以和亲爱的妻子约会了!
我们可以看到当两个妻子都能找到老公,老公是同一个人,而且都和老公约会了。
有一天,美国老婆希望老公和她出去约会,他心情愉悦,可是在他准备好好打扮自己的时候,中国妻子开开心心的跑了过来,“老公,和奴家约会去呗!”,这可怎么办啊,老公可没有韦小宝那么机灵,答应了美国老婆,可又不想让中国老婆失望,他陷入了思考之中!
在我们的老公的实例创建方法中,
if (instance == null) {
instance = new Husband();
}
美国老婆申请了老公实例,这时候老公实例还没有创建,所以instance ==null,那么老公会创建出一个实例来,可是还没等老公创建出实例,中国老婆也过来申请老公了,这时候老公还是没有创建出来,所以instance还是null,老公还会创建出来,这样就创建出了2个老公实例,可老公明明就是1个啊。在我们的代码中,如果有多个线程同时访问老公创建实例的方法,就不满足我们单例模式的单例了。
我们需要为实例过程加锁,修改老公类:
//单例,老公类
public class Husband {
static Lock lock = new ReentrantLock();
// 老公只有一个
private static Husband instance;
private Husband() {
}
public static Husband getInstance() {
//这里判断是为了防止频繁的进行加解锁
if (instance == null) {
//加上锁
lock.lock();
//创建实例,并且能够防止多次创建实例
if (instance == null) {
instance = new Husband();
}
//解锁
lock.unlock();
}
return instance;
}
public void show() {
System.out.println("老公可是一个安静的美男子!");
}
public void date() {
System.out.println("美男子可以和亲爱的妻子约会了!");
}
}