单利模式定义:核心结构中只包含一个被称为单例的特殊类。通过单例模式可以保证系统中一个类只有一个实例。即一个类只有一个对象实例。

一、常见需求场景


  三毛:“小白,你对后台返回来的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出来的静态实例不就浪费内存资源了嘛”

  小白:啪啪啪(敲键盘

Android 单利toast Android 单利一层判断_单例模式

),我改成下面那样子,毛毛哥看看这下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.内存开销减少了,降低多个对象访问同一个资源造成并发问题情况