确保一个类中只有一个实例,并且自行实例化向整个系统提供这个实例
适用于所创建的对象过于消耗资源,如访问IO、数据库等资源时,可使用单例模式,确保该类只有一个实例。如ImageLoader,其中包含线程池、缓存系统、网络请求等,很消耗资源,这种不能自由构造对象的情况,就是单例模式的使用场景
public class SingleTask {
//创建实例
private static SingleTask singleTask = new SingleTask();
private SingleTask(){}
public static SingleTask getInstance(){
return singleTask;
}
}
饿汉模式是线程安全的,并且在类加载的时候进行初始化,所以在类加载过程中相对较慢的,但是在使用的比较快
public class SingleTask {
private static SingleTask singleTask;
private SingleTask(){}
public static SingleTask getInstance(){
if (singleTask == null) {
//判断为null,才创建
singleTask = new SingleTask();
}
return singleTask;
}
}
懒汉模式声明静态对象,并在使用的时候才初始化,节约资源,但是第一次调用的时候需要初始化比较消耗时间,并且这种写法在多线程中是不安全的,由此可引申第三种写法
public class SingleTask {
private static SingleTask singleTask;
private SingleTask(){}
//加上关键字synchronized,保证线程安全
public static synchronized SingleTask getInstance(){
if (singleTask == null) {
singleTask = new SingleTask();
}
return singleTask;
}
}
这种写法能够在多线程中很好的工作,但是每次调用getInstance方法时都需要进行同步,造成资源的浪费和性能上的一些影响,同时,这种同步在大部分情况下是不会用到的,所以不建议使用这种方式
public class SingleTask {
//使用volatile 关键字保证双重检查模式的正确性
private volatile static SingleTask singleTask = null;
private SingleTask(){}
public static SingleTask getInstance(){
if (singleTask == null) {
//双重检查
synchronized (SingleTask.class) {
if (singleTask == null) {
singleTask = new SingleTask();
}
}
}
return singleTask;
}
}
这种写法在getInstance方法中对singleTask 进行了两次判空,第一次是为了不必要的同步,第二次是在singleton等于null的情况下才创建实例
public class SingleTask {
private SingleTask(){}
public static SingleTask getInstance() {
return SingleTaskHolder.sInstance;
}
//静态内部类
private static class SingleTaskHolder {
private static final SingleTask sInstance = new SingleTask();
}
}
双重检查模式实现的单例在大部分的情况下都是可以正确使用的,但是在比较复杂的情况下会偶尔失效,所以推荐使用静态内部类单例模式,第一次加载SingleTask 类时并不会初始化sInstance,在调用getInstance( )方法是才会加载SingleTaskHolder 类,进而初始化sInstance,这种方法既能保证线程的安全,也能保证单例对象的唯一想,同时也延迟了单例的实例化,所以是推荐使用这种单例实现方法
public enum SingleTask{
/**
* 定义一个枚举的元素,其代表的是SingleTask的一个实例
*/
INSTANCE;
/**
* 单例自己的操作
*/
public void operation() {
//相关功能
}
}
枚举实例的创建是线程安全的,并且在任何情况下都是单例,上述讲的几种单例模式实现中,有一种情况下他们会重新创建对象,那就是反序列化,将一个单例实例对象写到磁盘再读回来,从而获得了一个实例。反序列化操作提供了readResolve方法,这个方法可以让开发人员控制对象的反序列化
public class SingleTaskManager {
private static Map objMap = new HashMap<>();
private SingleTaskManager() {}
//将多种单例类型统一到一个管理器中
public static void registerService(String key,Object instance) {
if ((!objMap.containsKey(key))) {
objMap.put(key,instance);
}
}
//根据key来获取对应的对象
public static Object getService(String key) {
return objMap.get(key);
}
}
这种写法,可以通过统一的接口获取操作,降低使用成本,隐藏具体实现,降低了耦合度
我们先来看看Android中Context的继承结构图
图片来源:https://blog.csdn.net/qinjuning/article/details/7310620
由于ContextImpl是保护文件,所以在IDE中是看不到的,可以使用Source Insight查看源码
public class ContextWrapper extends Context {
Context mBase;
public ContextWrapper(Context base) {
mBase = base;
}
protected void attachBaseContext(Context base) {
if (mBase != null) {
throw new IllegalStateException("Base context already set");
}
mBase = base;
}
.......
}
简而言之,Context的两个子类分工明确,其中ContextImpl是Context的具体实现类,ContextWrapper是Context的包装类。Activity,Application,Service虽都继承自ContextWrapper(Activity继承自ContextWrapper的子类ContextThemeWrapper),但它们初始化的过程中都会创建ContextImpl对象,由ContextImpl实现Context中的方法
我们跟进ContextImpl源码:
class ContextImpl extends Context {
.......
//ServiceFetcher 通过getService获取服务对象
static class ServiceFetcher {
int mContextCacheIndex = -1;
public Object getService(ContextImpl ctx) {
ArrayList
从ContextImpl类中可以看到,虚拟机在以第一次加载类文件时会注册各种ServiceFatcher,这些服务会以键值对的方式存储在一个HashMap中,使用时可以调用getSystemService( )方法获取对应的ServiceFatcher,然后通过ServiceFatvher的createService方法创建服务对象,然后将该对象缓存到列表中,下次使用可直接从缓存中获取,避免重复创建对象。这其实就是上面我们所说的第七种单例创建方法即使用容器实现单例模式