先来三个问题:
1.什么是单例模式?
2.为什么要使用单例模式,优点是什么
3.什么情况下适合使用单例模式
先来回答第1个问题:
由定义可以看到单例模式的特点:
1.单例类有且只能有一个实例
2.单例类必须自己创建自己的唯一的实例
3.单例类必须给所有其他对象提供该实例
第2个问题:为什么要使用单例模式,其优点是什么
查了一下stackOverflow上面的一些解答,无外乎几种:
1.
可以有效避免其他对象访问单例对象的副本,确保了所有对象都访问这唯一的资源
(PS:上面那句略拗口,有人举了一个很有意思的例子,例如,地球只有一个,为了保证你不是活在你自己new出来的地球上,那么只有使用单例模式,这样大家就可以共享同一个地球了。这种单例是自然而然的,比如要访问同一个打印机,写同一个日志文件,访问同一个配置文件等等)
因为类控制了实例化过程,所以类可以灵活更改实例化过程。(这句话不是特别明白,求解惑,不过解答者后面做了一个解释:you can change your mind and manage any number of instances.我觉得所谓的灵活性只是控制数量的话,简直弱爆了)
《java与模式》一书中,作者也对单例作了总结:
1.节约内存,有利于垃圾回收(GC)
2.可以通过线程同步来控制资源的并发访问
3.作为通信媒介使用,可在不建立直接关联条件下,让多个不相关的线程或进程实现通信
最后一个问题:
这个问题,噗~大牛们在stackOverFlow上吵得太热闹了,各说纷纭,但是人气最高的是:单例模式纯属扯淡,到现在也没有一个让人信服的理由(╯‵□′)╯︵┻━┻
大家还是看原帖吧,很有意思
http://stackoverflow.com/questions/228164/on-design-patterns-when-to-use-the-singleton
http://misko.hevery.com/2008/08/25/root-cause-of-singletons/
好了,虽然第三个问题有点搬石头砸自己脚的,还是硬着头皮往下讲吧
常用的单例模式有三种:恶汉模式,懒汉模式,登记模式
还有三种高大上的(今天先入门...):双重校验锁模式、枚举、静态内部类
1.恶汉模式
//恶汉式单例类.在类初始化时,已经自行实例化
public class Singleton1 {
//私有的默认构造子
private Singleton1() {}
//已经自行实例化
private static final Singleton1 single = new Singleton1();
//静态工厂方法
public static Singleton1 getInstance() {
return single;
}
}
2.懒汉模式
//懒汉式单例类.在第一次调用的时候实例化
public class Singleton2 {
//私有的默认构造子
private Singleton2() {}
//注意,这里没有final
private static Singleton2 single=null;
//静态工厂方法
public synchronized static Singleton2 getInstance() {
if (single == null) {
single = new Singleton2();
}
return single;
}
}
3.登记模式
import java.util.HashMap;
import java.util.Map;
//登记式单例类.
//类似Spring里面的方法,将类名注册,下次从里面直接获取。
public class Singleton3 {
private static Map<String,Singleton3> map = new HashMap<String,Singleton3>();
static{
Singleton3 single = new Singleton3();
map.put(single.getClass().getName(), single);
}
//保护的默认构造子
protected Singleton3(){}
//静态工厂方法,返还此类惟一的实例
public static Singleton3 getInstance(String name) {
if(name == null) {
name = Singleton3.class.getName();
System.out.println("name == null"+"--->name="+name);
}
if(map.get(name) == null) {
try {
map.put(name, (Singleton3) Class.forName(name).newInstance());
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
return map.get(name);
}
//一个示意性的商业方法
public String about() {
return "Hello, I am RegSingleton.";
}
public static void main(String[] args) {
Singleton3 single3 = Singleton3.getInstance(null);
System.out.println(single3.about());
}
}
这里看到构造函数都被封装成 非public,原因是很容易理解的:为了阻止其他对象创建实例(说好的单例)