JAVA设计模式——单例模式
来源:互联网 发布:win10网络图标不能运行 编辑:程序博客网 时间:2024/06/06 00:56
转载请注明出处:http://blog.csdn.net/qq_35744386/article/details/78729154
设计模式–>单例模式
在整个系统中某一个类仅仅提供一个唯一的实例对象。一般我们会在这个类中写一个getInstence()方法来获取这个唯一实例对象。
实现方式分类
懒汉式单例类、饿汉式单例类、枚举单例类。
在Android开发中,我们也常常会用到SharePrefresence,一般来说将储存偏好的逻辑都存放在一个类中会比较好管理,对外只提供一个存储方法和一个读取方法即可,这时我们就可以用到单例模式。下面我们来看看具体实现。
懒汉式单例类
public class SharedPreferenceInstance { private SharedPreferences mPreferences; private static SharedPreferenceInstance mInstance = null; //常量key,用来储存或取出数据 private static final String MAIN_CONTENT = "MAIN_CONTENT"; //私有化构造方法,避免外部调用 private SharedPreferenceInstance() { mPreferences = PreferenceManager.getDefaultSharedPreferences(MyApplication.getApp().getApplicationContext()); } //外部获取单例方法 public static SharedPreferenceInstance getInstance() { if (mInstance == null) { synchronized (SharedPreferenceInstance.class) { if (mInstance == null) { mInstance = new SharedPreferenceInstance(); } } } return mInstance; } //保存 public void saveContent(String content) { if (mPreferences == null) return; mPreferences.edit().putString(MAIN_CONTENT, content).apply(); } //读取 public String getContent() { if (mPreferences == null) return null; return mPreferences.getString(MAIN_CONTENT, null); }}
分析:可以看到getInstance()方法中,如果mInstance = null即没有实例化过,就新建一个。否则就将以前新建的实例返回。(同步相关知识文末有详细讲解)
饿汉式单例类
public class SharedPreferenceInstance { private SharedPreferences mPreferences; //声明时就新建实例 private static SharedPreferenceInstance mInstance = new SharedPreferenceInstance(); //常量key,用来储存或取出数据 private static final String MAIN_CONTENT = "MAIN_CONTENT"; //私有化构造方法,避免外部调用 private SharedPreferenceInstance() { mPreferences = PreferenceManager.getDefaultSharedPreferences(MyApplication.getApp().getApplicationContext()); } //外部获取单例方法 public static SharedPreferenceInstance getInstance() { return mInstance; } //保存 public void saveContent(String content) { if (mPreferences == null) return; mPreferences.edit().putString(MAIN_CONTENT, content).apply(); } //读取 public String getContent() { if (mPreferences == null) return null; return mPreferences.getString(MAIN_CONTENT, null); }}
分析:可以看到与懒汉式单例类不同的是,在单例声明的同时就直接新建实例。在getInstance()方法中,直接返回该单例。
饿汉式与懒汉式是个形象的比喻,懒汉式当你需要我的时候我才新建实例;饿汉式不管你需不需要我一开始就新建实例。
枚举单例类
使用枚举来实现单例会更加简单.
public enum SharedPreferenceInstance { mInstance; private SharedPreferences mPreferences; //常量key,用来储存或取出数据 private static final String MAIN_CONTENT = "MAIN_CONTENT"; SharedPreferenceInstance() { mPreferences = PreferenceManager.getDefaultSharedPreferences(MyApplication.getApp().getApplicationContext()); } //外部获取单例方法 public static SharedPreferenceInstance getInstance() { return mInstance; } //保存 public void saveContent(String content) { if (mPreferences == null) return; mPreferences.edit().putString(MAIN_CONTENT, content).apply(); } //读取 public String getContent() { if (mPreferences == null) return null; return mPreferences.getString(MAIN_CONTENT, null); }}
分析:使用枚举从根本上杜绝重复创建对象,枚举的单例性是由JVM来保证的。
线程同步与单例模式中的内存优化
线程同步:通俗的讲就是在同一时间保证仅有一条线程可以访问某个代码块。
大家都知道java中要想保证线程同步只需要在方法上加锁即可:
public synchronized void function(){ //具体方法}
举个例子:在商场买衣服,同一个试衣间同一时间仅仅只会有一位顾客正在使用,其实这就是一个同步的问题,我们看看是如何做到的,当然就是在试衣间的门上加了一道锁,当其他顾客看到试衣间门锁住了就自然明白有人正在使用,就会等待他人使用完毕再进入。拿到我们程序来说,当某一条线程访问了function方法,它就会在门上上锁,当其他线程尝试访问这个方法时,发现已经上了锁,就会排队等候,直到之前的线程将function方法执行完毕。好了我们对同步有了基本认识,那么我们就趁热打铁继续分析一下懒汉式单例类中的线程同步吧。
相信很多人都知道懒汉式单例类单例方法(getInstance()方法)还有这种写法:
public static synchronized SharedPreferenceInstance getInstance() { if (mInstance == null) mInstance = new SharedPreferenceInstance(); return mInstance;}
我们可以看到这种写法是直接在方法上加入了同步锁,使得同一时间,仅有一个线程能够进入这个方法,接下来我们分析一下这种写法的弊端。先拿之前商场买衣服的例子来说,这种写法就好比是我进入某一商铺后,就把商铺的大门给关上,等我选好衣服,试穿完毕,结账走出大门以后,下一个顾客才能进入这个商铺选购衣服。显然这样做是不合理,只要商铺不打烊,进入商铺什么时候都是可以的,即使现在有顾客在试穿衣服,但是选择衣服这个事请别个顾客也是可以同时进行的,只有使用试衣间时需要同步。再回到我们的代码中分析,其实多个线程是允许同时进入这个方法的,我们需要同步的地方仅仅是在新建单例实例的时候,如果方法中还有其他代码,多个线程大可把其他代码执行完毕,到了要新建单实例时,再排队等候。下面我们结合代码在代码中注释解释上面的双层判断:(假设现在有A B C 三个线程同时要访问getInstance方法。并且A是第一个进入同步代码块的线程,B进入时A还未解锁(同步代码还未执行完毕),C进入时A已经解锁(同步代码已经执行完毕))
public static SharedPreferenceInstance getInstance() { //当C进入方法,发现mInstance 不为null,就直接返回mInstance if (mInstance == null) { //B 进入时由于A还未创建完毕,发现mInstance为null 进入分支 发现下面代码 被锁了 就等待... synchronized (SharedPreferenceInstance.class) {//这部分代码被A锁住 //当A创建完毕后B进入同步代码块 ,B又一次判断,发现mInstance已经不为null了,就直接解锁,返回mInstance if (mInstance == null) { mInstance = new SharedPreferenceInstance();//A 正在执行创建... } } } return mInstance; //通过以上的双层判断加同步锁就能保证实例唯一,并且可以保证线程并不需要都走同步代码块,提高了代码的执行效率。}
单例模式中的内存优化
首先我们分析一下饿汉式单例类,可以发现不管我们现在需不需要这个单例,程序在一开始就直接new 出了实例,这就会导致一个问题,浪费了内存空间。
我们再来看看懒汉式单例类,与饿汉式相比虽然一开始没有直接new出对象,但是却用到了线程同步降低了代码的执行效率。
那么,有没有既不浪费空间,又不会降低效率,并且还能保证单例性的方法呢?
答案肯定是有的,比如如下这样:
public class SharedPreferenceInstance { private SharedPreferences mPreferences; //常量key,用来储存或取出数据 private static final String MAIN_CONTENT = "MAIN_CONTENT"; private SharedPreferenceInstance() { mPreferences = PreferenceManager.getDefaultSharedPreferences(MyApplication.getApp().getApplicationContext()); } //外部获取单例方法 public static SharedPreferenceInstance getInstance() { return Holder.mInstance; } // 静态内部类 持有单例对象 private static class Holder { private static SharedPreferenceInstance mInstance = new SharedPreferenceInstance(); } //保存 public void saveContent(String content) { if (mPreferences == null) return; mPreferences.edit().putString(MAIN_CONTENT, content).apply(); } //读取 public String getContent() { if (mPreferences == null) return null; return mPreferences.getString(MAIN_CONTENT, null); }}
分析:可以发现与之前相比我们增加了一个静态内部类Holder,该类仅有一个外部类的成员,并在声明的时候就初始化了。大家都知道,静态内部类相当于其外部类的成员,只有在第一次被使用的时候才被会装载。当getInstance方法第一次被调用的时,Holder类也会被第一次读取,就会导致Holder类的装载与初始化,这时就会初始化Holder类的静态域,从而mInstance也会得到创建,由于静态域只会在类装载时初始化一次,并由虚拟机来保证它的线程安全,所以这种方式就巧妙的达到了我们之前的需求。
当然使用枚举还是更方便、简洁、高效的创建单例的方式。
- Java 设计模式—单例模式
- java设计模式—单例模式
- java设计模式—单例模式
- java设计模式—单例模式
- Java设计模式—单例模式
- Java设计模式—单例模式
- Java设计模式—单例模式和模板模式
- JAVA设计模式——单例设计模式
- Java设计模式——单例设计模式
- Java设计模式——单例设计模式
- java设计模式——单例设计模式
- java设计模式——单例设计模式
- Java 设计模式—单例设计模式总结
- java——单例设计模式
- Java——单例设计模式
- Java设计模式——单例
- Java——单例设计模式
- Java—JAVA设计模式之单例模式
- 欢迎使用CSDN-markdown编辑器
- Centos搭建ceph+++七、准备OSD
- web.xml文件详解
- xposed 5.0以上资源地址
- 当自定义网络库遇上Rxjava2
- JAVA设计模式——单例模式
- java就业分析
- 各图形的画图
- 关于本博客及学习情况纪录
- android 定时任务(TimerTask和Handler对比)
- spring cloud 刷新配置中心的配置以及svn操作
- 设计模式基础——抽象类、抽象方法、接口与虚方法
- vue基础之axios
- eChars实现哈尔滨市区县地图展示