设计模式(一)单例模式详解

愉快的开始自己的技术积累 ,设计模式中使用最普遍的单例模式开始;

设计模式中最为大家熟悉的必须是单例模式,项目中 必须 使用到的套路。首先陈述下我对 框架,模式的一些理解。

从学校出来,开始面试的时候张口框架,闭口模式,真的问道什么是框架,我只能死鸭子嘴硬的强调 MVC ,MVP 等等技术博客上看到名词。

有句话说的好,熟读百遍,其义自见。不管是 六大开发原则,还是二十三种设计模式,亦或是熟悉的开发流程MVC MVP 等,都是套路。是前人总结,切实可行的套路、可以让你沿着这一条条康庄大道追到小姐姐的技术、模式。你可以照本宣科,应用起来可能生硬,出格,但是只要追到小姐姐,你就成功了。所以编程和泡妹一样。知道你为什么还没有女朋友吗?是你的套路用错了地方。

我一直认为 六大开发原则 是总纲,相当于武林中的内功心法,而设计模式是外在表现,是功法。所以熟记原则至关重要。

1、优化代码的第一步             —— 单一职责原则

2、让程序更加稳定,灵活     —— 开闭原则

3、构建扩展性更好的系统     —— 里氏替换原则

4、让项目拥有变化的能力     —— 依赖倒置原则

5、系统有更高的灵活性        —— 接口隔离原则

6、更好的可扩展原则            —— 迪米特原则

言归正传:此文文章是总结主讲  单例模式

  1. 什么是单例模式;
  2. 意图;
  3. 简单实现;
  4. 单例模式的几种实现方式;
  5. 关于单利的类,实例化。
  6. 来来,面试了 你怕不怕。

1、什么是单例模式:百度百科中解释, 单例模式,是一种常用的软件设计模式。在它的核心结构中只包含一个被称为单例的特殊类。通过单例模式可以保证系统中,应用该模式的类一个类只有一个实例。即一个类只有一个对象实例

2、意图:保证一个类仅有一个实例,并提供一个访问它的全局访问点。

3、简单实现: 创建一个类的对象,私有化构造 private 修饰,对外提供一个唯一获取对象的静态方法。


public class SingleObject {
 
   //创建 SingleObject 的一个对象
   private static SingleObject instance = new SingleObject();
 
   //让构造函数为 private,这样该类就不会被实例化
   private SingleObject(){}
 
   //获取唯一可用的对象
   public static SingleObject getInstance(){
      return instance;
   }
 
}

4、单例模式的写法五花八门,功能各不相同。

(一):懒汉式  线程不安全写法;

不主动进行初始化,只有第一次调用的时候,才被动初始化话,但是因为没有枷锁,在多线程中基本不能使用。

/*这种方式是最基本的实现方式,这种实现最大的问题就是不支持多线程。因为没有加锁 synchronized,
 *所以严格意义上它并不算单例模式
 */

public class Singleton {  
    private static Singleton instance;  
    private Singleton (){}  
  
    public static Singleton getInstance() {  
    if (instance == null) {  
        instance = new Singleton();  
    }  
    return instance;  
    }  
}

(二):懒汉式,线程安全写法

懒汉式的优化版本,加一个 synchronized ,保证其在多线程中正常的工作,但 枷锁的同时,也影响了其效率。说实在的,Andorid中使用 多线程同时访问的场景微乎其微。


public class Singleton {  
    private static Singleton instance;  
    private Singleton (){}  
    public static synchronized Singleton getInstance() {  
    if (instance == null) {  
        instance = new Singleton();  
    }  
    return instance;  
    }  
}

(三):恶汉式,基于classloader 加载机制。避免了多线程同步的问题,但是容易产生垃圾对象。


public class Singleton {  
    private static Singleton instance = new Singleton();  
    private Singleton (){}  
    public static Singleton getInstance() {  
    return instance;  
    }  
}

(四):双检锁/双重校验锁、安全且在多线程情况下能保持高性能。

//volatile 开发中基本很少使用这个关键字,大概意思是保证并发编程的时候类的 
// 1、保证可见性
// 2、保证有序性

public class Singleton {  
    private volatile static Singleton singleton; 
 
    private Singleton (){}  

    public static Singleton getSingleton() {  
    if (singleton == null) { 
 
        synchronized (Singleton.class) { 
 
        if (singleton == null) {  

            singleton = new Singleton();  
        }  
      }  
    }  
    return singleton;  
    }  
}

(五)、关于单例模式的书写,大概有三种。但是基本没有用过了。有想了解的朋友可以自行百度。个人觉得,后面的写法大概就是另辟蹊径。简单粗暴的陈述 我很吊。。。像我这种平凡玩家,还是不去和它们过不去了。

5、关于单利的类,实例化

我使用单例模式的地方很单一,保存用户常用数据,保存APP 常用数据。方便你在程序任何地方都可以拿到这些常用信息,便于修改,保存。并在此调用。

这种使用场景,不可避免的需要序列化,而Andorid 中对类进行序列化的方式有两种。Serializable 和 Parcelable

其中Serializable是Java自带的,而Parcelable是安卓专有的。


Serializable使用IO读写存储在硬盘上。序列化过程使用了反射技术,并且期间产生大量的临时对象。优点代码少。

Parcelable是直接在内存中读写,我们知道内存的读写速度肯定优于硬盘读写速度,所以Parcelable序列化方式性能上要优于Serializable方式很多。但是代码写起来相比Serializable方式麻烦一些。


Serializable的使用:

public class Person implements Serializable {

    private static final long serialVersionUID = -3139325922167935911L;
    // 首先Serializable 是一个javaSE标记接口,所产生的序列化值和所发生序列化的bean
    // 有关,如果没有设置此值,在发生反序列化的时候,改Bean的成员变量发生变更,可能导致
    // 反序列化失败。
    


    private int age;
    private String name;

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

}

Parcelable 的使用

(一):首先实现 Parcelable 接口需要重写的几个方法。

public final class Rect implements Parcelable {
    public int left;
    public int top;
    public int right;
    public int bottom;

    private static Rect rect = new Rect();

    public static final Parcelable.Creator<Rect> CREATOR = new
            Parcelable.Creator<Rect>() {
                public Rect createFromParcel(Parcel in) {

                //从序列化中的对象创建原始对象

                    return new Rect(in);
                }

                public Rect[] newArray(int size) {

                //创建指定长度的原始对象数组

                    return new Rect[size];

                }
            };

    private Rect() {
    }

    public static Rect getIntent(){
         return rect;
    }    

    private Rect(Parcel in) {
        readFromParcel(in);
    }

    public void writeToParcel(Parcel out) {
    //
        out.writeInt(left);
        out.writeInt(top);
        out.writeInt(right);
        out.writeInt(bottom);
    }

    public void readFromParcel(Parcel in) {
        left = in.readInt();
        top = in.readInt();
        right = in.readInt();
        bottom = in.readInt();
    }
}

OK,是不是没有任何难度,告诉你。如果你只知道这些出去面试。肯定碰一鼻子灰。

来道附加题:请问我对一个类进行单利,不管是懒汉 饿汉,还是双校验,我进行了序列化,之后再进行反序列化。那么请问我拿到这个类,还是唯一的吗?

?????

我刚听到这个问题的时候一脸懵逼,说实在的开发中并且没有注意这些问题,不过不会我们可以猜,大胆猜测,我麻痹从硬盘中取出数据,对于整个层序来说肯定不是唯一的了啊。

那么,怎么保证唯一;

好吧:答案是 重写readReslove,如此可以保证。至于原因,关心的建议百度。

public class ReadResolveDemo implements Serializable {

    private static final long serialVersionUID = 1L;

    private static final ReadResolveDemo INSTANCE = new ReadResolveDemo();
 
    private ReadResolveDemo() {
    }
 
    public static ReadResolveDemo getInstance() {
        return INSTANCE;
    }
 
    private Object readResolve() {
        return INSTANCE;
    }
 
}

那么,我使用反射拿到这个类,不是也不能保证此类的单一。

怎么那么多那么,你有病啊,自己写的程序,自己给自己挖坑。不过吐槽归吐槽,毕竟人家反编译,或者压根是你以SDK方式提供别人引用的呢。所以。。

保证 反射拿不到类就可以了吧,怎么保证呢:上代码

 
public class ReadResolveDemo implements Serializable {
    private static final long serialVersionUID = 1L;
    private static final ReadResolveDemo INSTANCE = new ReadResolveDemo();
    private static boolean flag = false;
    
    synchronized (Elvis.class) {

    System.out.println(" try to instance");

        if (flag == false) {

            System.out.println("first time instance");

            flag = !flag;

         } else {

            throw new RuntimeException("单例模式被侵犯!");

         }

       }

    }

    private ReadResolveDemo() {
    }
 
    public static ReadResolveDemo getInstance() {
        return INSTANCE;
    }
 
    private Object readResolve() {
        return INSTANCE;
    }

}

文章的最后让我们举个栗子,这是我和朋友开玩笑讲解,的想出来。

怎么理解上述一系列操作呢?


一群工科生班级里,突然有一个女神级妹子,经过 同学A 的各种殷勤加跪舔,终于让你拿下了 妹子,作为自己的女朋友。

都是自己女朋友了,我肯定不能让班级里一群牲口在去调戏了啊, 加上单例,果断的想要约我女朋友社交,必须通过我才行,不然你们联系不上。因为他的电话只有我知道。

可是放假了,妹子要回家了(序列化了),我的天啊,这下子脱离我的掌控了。肿么办,不行手机给开个呼叫转移。你打电话联系就到我电话上了(重写 ReadReslove )

可是妹子要毕业了,父母给安排了一系列的相亲,那哪成,肿么办。哎、我去破坏他们。嗯,先加个判断,再抛个异常。老兄你放弃吧,妹子是我的。你通过Android爸爸提供的反射,也不行。


下一篇正在整理,关于使用SP 存储用户数据的工具类。一直在用,感觉挺带感的。就上传github上,供大家参详。

猜你喜欢

转载自blog.csdn.net/qq_30974087/article/details/81560439