单例模式可能在开发中经常碰到,应用也很广泛,很多开源框架提供第一个初始化相关资源的类都会是该模式,由于该类中可能存在线程池、缓存系统、网络请求等,很消耗资源,不应该创建多个对象,这时候就需要用到单例模式。
尽管单例模式的代码实现逻辑很简单,但是很多都是只知其一,不知其二。导致在自己的代码里面不能很合理的使用该模式。
接下来我们通过以下几点去分析它:
1.什么是单例模式?
也就是说在程序运行的时候,如果某个对象是通过该模式来创建的话,它在程序运行的整个过程中有且只能有一个实例存在。
2.我们再那些情况下使用单例模式
1.在我们开发的过程当中某个对象可能要被多次创建,多次创建某个很有可能引起内存泄露,如果垃圾回收器不能全部回收的情况下。
2.如果初始化某个对象的时候,在其内部又初始化了大量的其他对象。
下面举个例子来说明一下第二种情况:
例如通过Picasso框架去加载图片资源。下面是代码:
//上面是单例模式中双重校验锁模式,并且通过volatile关键字解决了此种模式失效的问题。
static volatile Picasso singleton = null;
public static Picasso with(Context context) {
if (singleton == null) {
synchronized (Picasso.class) {
if (singleton == null) {
singleton = new Builder(context).build();
}
}
}
return singleton;
}
/**
*在此框架中它将对象资源的初始化通过内部类Builder来完成,降低代码的耦合度,可扩展性
*/
public static class Builder {
public Builder(Context context) {
if (context == null) {
throw new IllegalArgumentException("Context must not be null.");
}
this.context = context.getApplicationContext();
}
/** Create the {@link Picasso} instance. */
public Picasso build() {
Context context = this.context;
if (downloader == null) {
downloader = Utils.createDefaultDownloader(context);
}
if (cache == null) {
cache = new LruCache(context);
}
if (service == null) {
service = new PicassoExecutorService();
}
if (transformer == null) {
transformer = RequestTransformer.IDENTITY;
}
Stats stats = new Stats(cache);
Dispatcher dispatcher = new Dispatcher(context, service, HANDLER, downloader, cache, stats);
return new Picasso(context, dispatcher, cache, listener, transformer, requestHandlers, stats,
defaultBitmapConfig, indicatorsEnabled, loggingEnabled);
}
}
通过上面的代码我们看到以下信息:
a.使用了单例模式中双重校验锁模式,并且通过volatile关键字解决了此种模式失效的问题。
b.通过上面的代码我们看到如果Picasso 类不使用单例模式,每一次初始化都要去执行build方法中的代码,这样一来每次都会有大量的对象被创建。