在现实生活中,总有些事物有且只有一个对象的,例如皇帝,名家的真迹。
对于这类事物在设计类的时候就要考虑到,这种类只能生成一个对象,如果生成了多个对象就与现实世界的规律不符了。
我们应该如何来控制单例的设计:
(1)首先我们不能把让用户想使用其他类一样去直接使用new 调用类的构造方法,不能让用户调用构造方法,即将构造方法设置为私有属性,这样用户就不能用new关键字实例化对象了,与此同时在类中必须要设置一个可以返回类对象实例的方法,否则将会无法实现类的实例化。
(2)要考虑到拷贝构造函数,在java中类要实现Cloneable接口还可以完成拷贝,只要不实现Cloneable方法即可。但在c++却不同,c++中要把拷贝构造函数设置为私有。
(3)考虑线程安全问题
(4)可以通过反射机制实现单例,具体请参看http://blog.csdn.net/nifenggege8888/article/details/40827249,工厂方法模式的第三种扩展
下面看一个线程安全的例子:
首先定义一个皇帝类,代码如下:
package com.feng.singleton;
public class Emperor {
//初始化一个皇帝,让用户都是用这一个皇帝实例
private static final Emperor emperor = new Emperor();
private Emperor()
{
//皇帝只应该有一个,这就是一个单例,构造方法设置为private
}
//提供一个返回实例对象的方法
public static Emperor getInstance()
{
return emperor;
}
//皇帝有一个说话的方法,最好是static
public static void say()
{
System.out.println("皇帝开始说话了。。。。。");
}
}
接着定义一个大臣类,大臣每天来朝拜皇帝,朝拜的都是同一个人
package com.feng.singleton;
public class Minister {
public static void main(String[] args)
{
//大臣三天都朝拜一个皇帝,虽然多次获取对象,但都是同一个对象
for(int day=0; day<3; day++)
{
Emperor emperor = Emperor.getInstance();
emperor.say();
}
}
}
我们稍对皇帝类进行一下改造
package com.feng.singleton;
public class Emperor {
//初始化一个皇帝,让用户都是用这一个皇帝实例
private static Emperor emperor = null;
private Emperor()
{
//皇帝只应该有一个,这就是一个单例,构造方法设置为private
}
//提供一个返回实例对象的方法
public static Emperor getInstance()
{
if(null == emperor)
{
emperor = new Emperor();
}
return emperor;
}
//皇帝有一个说话的方法,最好是static
public static void say()
{
System.out.println("皇帝开始说话了。。。。。");
}
}
在上面的代码中,如果实在单线程中运行时没有问题的,如果是在多线程中,就有可能会创建出多个对象出来,例如,第一个线程A运行到if(null == emperor),一开始emperor是为空,在走emperor = new Emperor();这条语句之前,另一个线程B运行到了if(null == emperor);这条语句,此时线程A还没有创建Emperor对象,所以线程B也会走到emperor = new Emperor(); 这样的话线程A,B都会建造出一个Emperor对象出来,这就和单例相违背了。所以这种写法是线程不安全的例子。
解决线程不安全问题的方法有很多:
(1)在getInstance()方法前加上synchronized关键字
(2)也可以在geInstance()方法里面加上synchronized来实现(都是使用线程同步机制来解决)
(3)使用本贴中实现的线程安全代码(简单,实用)
单例模式的优点:
(1)单例模式在内存中只有一个对象,节省内存,减少系统的开销
(2)可以避免对资源的多重占用
(3)可以在系统中设置全局访问点,优化和共享资源访问
单例模式的缺点:
(1)单例模式一般没有借口,扩展困难。一般只要扩展就要修改代码。接口对单例模式没有意义,因为单例模式就是自己实现实例化。
(2)单例模式对测试不利。如果单例模式没有完成,其他模块是没法测试的。
(3)单例模式与单一职责原则冲突
单例模式的使用场景:
(1)要求生成唯一序列号的坏境
(2)在整个项目中需要一个共享访问点或共享数据
(3)创建一个对象需要消耗资源过多时。
(4)需要定义大量的静态常量和静态方法的环境
关于c++实现的单例模式,请参考另一篇文章:
http://blog.csdn.net/nifenggege8888/article/details/25425859