一. 单例模式
1.实现单例模式的几个关键点
a) 构造函数不对外开发,一般是Private
b) 通过静态方法或者枚举返回单例类对象
c) 确保单例类的对象有且只有一个,尤其在多线程环境下
d) 确单例在反序列化时不会被重新构建对象
2. 单例模式的几种实现
1.懒汉式单例模式
//懒汉式单例类.在第一次调用的时候实例化自己
publicclass Singleton {
private Singleton() {}
private static Singleton single=null;
//静态工厂方法
public static Singleton getInstance(){
if (single == null) {
single = new Singleton();
}
return single;
}
}
风险:多线程下回出现多个实例,不建议使用。
三种方法解决问题:
1、在getInstance方法上加同步:效率低,带来不必要的同步开销
publicstatic synchronized Singleton getInstance() {
if (single == null) {
single = new Singleton();
}
return single;
}
2.双重检查锁定:效率高
public static Singleton getInstance() {
if (singleton == null){
synchronized(Singleton.class) {
if (singleton== null) {
singleton =new Singleton();
}
}
}
return singleton;
}
说明:第一重判断是为了避免不必要的同步,第二重判断则是为了保证是单个实例对象。
优点:资源利用率高,只有第一次调用的时候才会生成实例,使用比较广泛。
缺点:第一次加载比较慢,另外还有些jdk版本问题。不过普通情况不用关注。
3.静态内部类:推荐
public class Singleton {
private static class LazyHolder {
private static final Singleton INSTANCE= new Singleton();
}
private Singleton (){}
public static final Singleton getInstance() {
return LazyHolder.INSTANCE;
}
}
这种比上面1、2都好一些,是《Java并发编程实践》推荐的方法。既实现了线程安全,又避免了同步带来的性能影响。解释下,当第一次加载Singleton的时候并不会直接去初始化LazyHolder类,只有在我们调用getInstance方法的时候才会去调用到该类,这时候回去初始化对象。
优点:保证线程安全,保证了单例的唯一性,又能起到延时加载的作用。
2.饿汉式单例模式
public class Singleton {
private Singleton() {}
private static final Singleton1 single = new Singleton1();
//静态工厂方法
public static Singleton getInstance() {
return single;
}
}
说明:饿汉式单例类.在类初始化时,已经自行实例化 ,会消耗一些资源,但是安全有效,天生线程安全。
3.枚举单例
public enum Singleton {
INSTANCE;
public void doSomething(){
//TODO
}
}
说明:这个是“java高效编程指南”《Effective Java》推荐的方法。
优点:简单,线程安全,任何时候都是一个实例对象。感兴趣的可以反编译看源码就知道了。另外最大的优点是可以避免被反序列化重新生成对象,我们在反序列化上面的单例模式的类的时候,即使它的构造方法是私有的,依然可以通过特殊途径去拿到它的对象,不过反序列化操作提供了一个钩子方法给我们,readResolve(),用这个可以控制在反序列化的时候还是原来那个对象,而不是重新生成。在单例中加入下面这个方法即可防止被反序列化生成多个对象(这个是复杂需求下的处理办法,实际我们很少碰到的,可以不必关于关心)。
private Object readResolve() throwsObjectStramException{
return instance;//返回我们自己的单例对象
}
另外:容器实现单例
这个就不多说了,用的更少,但是在源代码中我们会看到,它实际就是使用一个Map集合将所有的单例对象给储存起来Key-Values键值对形式保存起来,然后我们要拿某一个单例对象的时候就通过某个key来取。
3. 总结
在没有高并发的时候,其实上面的单例模式大多都是可以的,不过比较推荐使用--双重检查锁定和静态内部单例来做。
单例模式的优缺点
优点:
1. 只有一个实例对象,减少了内存开销,特别在对象频繁创建和销毁的时候,这个过程无法进行优化,单例模式就体现出优势了
2. 单例对象只有一个,那么当某个对象需要较多系统资源的时候就可以减少系统开销,让它永驻内存,谁用就拿去,没必要创建多个
3. 避免对资源的多重占用,比如读写某个文件,单例模式保证了安全性,不会发生同时读写问题
4. 可以设置全局的访问点,优化和共享资源的访问。比如可以设计一个类,它来负责全局的数据映射。
缺点:
1. 单例模式一般没有接口,扩展性很差,正常的只要你想扩展,就必须修改源代码
2. 单例对象如果持有Context,非常容易造成内存泄漏问题,因此要十分注意,最好给单例对象的Context是Application Context.