欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

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

程序员文章站 2024-02-22 13:20:59
...

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

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

 

从学校出来,开始面试的时候张口框架,闭口模式,真的问道什么是框架,我只能死鸭子嘴硬的强调 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上,供大家参详。

 

 

 

 

相关标签: 设计模式 单例