单利模式定义:核心结构中只包含一个被称为单例的特殊类。通过单例模式可以保证系统中一个类只有一个实例。即一个类只有一个对象实例。
一、常见需求场景
三毛:“小白,你对后台返回来的json数据解析是用json还是Gson吖”
小白:我是用的Gson解析后台返回来的数据,怎么了?
二、基本解决方法
三毛:“那你是怎样使用Gson的呢”
小白:因为我一般都是拿到后台给的json字符串数据,然后转换成实体类对象,就像下面拿到json字符串jsonStr,然后转换成实体类A
//初生牛犊不怕虎的(普通写法)
Gson gson = new Gson();
A aBean = gson.fromJson(jsonStr, A.class);
三、基本解决方法存在的问题
三毛:“我的白,你这样new,new,new,一两个没啥问题,但是几十个,几百个,会存在内存占用多和不同对象对同一个资源的操作的问题波”
小白:毛毛哥说得是,确实是这样,那我给Gson写一个类,然后调用它来解析,像下面那样
//饿汉式单例写法
public class GsonSingle {
private static Gson mGson = new Gson();
private GsonSingle() {//构造函数设置为private,防止外部程序通过new来构造
}
public static Gson getInstance() {
return mGson;
}
}
//调用gson单利类来解析
A aBean = GsonSingle.getInstance().fromJson(jsonStr, A.class);
三毛:”白啊,你这样确实解决了一直new,new,new的问题,假设你这个GsonSingle类不使用getInstance静态方法的时候,而是使用其他静态方法,那你静new出来的静态实例不就浪费内存资源了嘛”
小白:啪啪啪(敲键盘
),我改成下面那样子,毛毛哥看看这下Ok了吧
//懒汉式写法
public class GsonSingle {
private static Gson mGson = null;
private GsonSingle() {
}
public static Gson getInstance() {
if (mGson == null) {
mGson = new Gson();
}
return mGson;
}
}
三毛:“你这样写,正常来看是没什么问题,但是在多线程的情况下就会出现另外的问题,多线程并发,线程不安全的情况呢”;
小白:那我给它加个锁synchronized,变成下面那样子,怎么样,这下没什么好说的了吧;
//懒汉式枷锁写法
public class GsonSingle {
private static Gson mGson = null;
public static synchronized Gson getInstance() {
if (mGson == null) {
mGson = new Gson();
}
return mGson;
}
}
三毛:“嘿嘿,这样加锁确实处理了多线程并发的问题,但是当你次次获取单例都同步,那不和前面一样,造成资源浪费了吗,也就是说等于没啥子改进的啵”
小白:有点道理,啪啪啪(敲键盘),好了,调下位置,改成下面那样,这样一来就不会次次都因为同步造成资源浪费了吧
//懒汉式双重枷锁写法
public class GsonSingle {
private static Gson mGson = null;
private GsonSingle() {
}
public static Gson getInstance() {
//先检查实例是否存在,如果不存在才进入下面的同步块
if(mGson == null){
//同步块,线程安全地创建实例
synchronized(Gson.class){
//再次检查实例是否存在,如果不存在才真正地创建实例
if(mGson == null){
mGson = new Gson();
}
}
}
return mGson;
}
}
三毛:“嗯,是的,不过你这样写第一次加载类的时候还是需要同步,也就意味着第一次加载类会牺牲一点时间,你觉得还能改进嘛”
小白:我想不到其他改进的办法了
三毛:“嘿嘿嘿,可能你一时忘记了,其实可以用内部类来写单例,像下面这样”
//内部类单例写法
public class GsonSingle {
private GsonSingle() {
}
public static Gson getInstance() {
return GsonSingleHolder.gson;
}
private static class GsonSingleHolder {
private static final Gson gson = new Gson();
}
}
小白:握草,居然还有这种操作,三毛哥,你平常的单例都是用这中内部类的方式吗?
三毛:“大部分都是用的内部类写单例,也有时候偷懒,像你第一种方式直接在类里new一个静态对象,不过我还是看情况来选择,另外你上面的改进也是属于单例设计模式中的几种写法,其实还有网络江湖上流传的另外2中写法你可能没想到,那就是枚举单例和容器单例”
小白:嗷嗷嗷,和毛毛哥聊天又学到东西了,我看看枚举单例和容器单例去~~
四、单例模式和普通写法的区别
1.一个类只有一个实例;
2.自己创建这个实例;
3.整个系统只能用这个实例;
4.内存开销减少了,降低多个对象访问同一个资源造成并发问题情况。