设计模式篇(一)——单例模式

一、简介

单例模式是最简单也比较常见常用的设计模式。我们通常建立一个类,通过该类来创建实例,而有时候,我们仅仅需要这个类的一个实例就够了,例如各种工具类,其实现的效果都是一样的,我没有必要创建多个实例来占用系统资源。

定义:单例模式是一种将类的实例化限制为一个对象的设计模式。

使用场景:各种工具类,例如日志打印等,缓存和线程池,数据库连接等。

实现要素:

  1. 限制其只有一个实例。
  2. 全局都可以访问该实例。

二、实现

单例模式的实现方式有很多种。

1、经典实现
//Java 代码
class Singleton { 

    private static Singleton obj; 
    
    private Singleton() {} 
  
    public static Singleton getInstance() { 
        if (obj==null) 
            obj = new Singleton(); 
        return obj; 
    } 
} 
//Kotlin 代码
class Singleton {

    private constructor()
    
    companion object{
        private var obj: Singleton? = null
        val instance: Singleton
            get() {
                if (obj == null) obj = Singleton()
                return obj!!
            }
    }
}

这里我们对 Java 代码稍微分析一下,先看看有一个私有化的构造方法:private Singleton() {},这段代码避免了你在其他地方可以通过new Singleton()来创建实例。

然后定义一个私有化的Singleton实例,和一个初始化Singleton的实例并返回该实例的方法:getInstance()

我们的预想是可以通过Singleton.getInstance()来获取实例,所以将getInstance()这个方法设置为静态函数,由于该方法时静态方法,那么里面的相关属性也要成为静态变量,所以才有了上面的代码。

2、加一层安全

为了防止多个线程同时第一次访问,而造成的线程安全问题,我们要多加一个同步锁。

//Java 代码
 	... ...
    public static synchronized Singleton getInstance() 
    { 
        if (obj==null) 
            obj = new Singleton(); 
        return obj; 
    } 
    ... ...
//Kotlin 代码
	... ...
	val instance: Singleton
        @Synchronized
        get() {
            if (obj == null) obj = Singleton()
            return obj!!
        }
    ... ...
3、急切实例化
//Java 代码
class Singleton { 

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

这里在定义Singleton实例的时候,直接就初始化实例。因为虚拟机加载程序的时候,会先初始化静态值,所以能够保证线程是安全的。但是,最好不要这么做,如果你的类有一些静态变量,且在私有构造方法里面会有一些操作静态变量的代码,很可能会出现意想不到的后果。因为代码的执行循序可能不是你想的那样。

4、双重检查(推荐)
class Singleton { 

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

使用 volatile 关键词,保证了实例的原子性。那么为什么要加两层判断呢?

试想一下,当实例已经被初始化,我们还需要同步嘛?我们可以直接将实例拿过来使用,如果还是同步的话,那就是大大的浪费系统资源。通过双重检查,仅当第一次初始化实例的时候,才会同步,之后就不需要同步了。

5、Kotlin实现

Kotlin 给我们提供了一个关键词 object,这个关键词可以作为类的修饰,被修饰的类直接就是单例模式了。

object Singleton{
    
}

当我们查看该 Kotlin 代码的字节码,再转换成 Java 代码,就是如下所示:

public final class Singleton{
   public static final Singleton INSTANCE;

   private Singleton() {
   }

   static {
      Singleton var0 = new Singleton();
      INSTANCE = var0;
   }
}
6、其他实现

只要基于单例模式的原则,可以变着花样的实现。

  1. 我们甚至可以用 枚举 来创建对象,在 Java 中,enumclassinterface 是同一个级别的,枚举天生就是一个单例。
  2. 我们还可以利用一个 Map 集合,以 key&value 的形式,每一个 key 对应一个对象的方式来储存一个单例,当我们需要使用到某个单例,就通过 keyMap 中获取这个对象。

三、相关源码

Glide 作为当下比较流行的图片加载框架,想必大家都不会很陌生。而图片加载工具,我们往往只需要一个实例,在任何地方,只需要获得该实例来进行图片的加载,我们复习一下Glide如何使用:
Glide.with(context).load(url).into(imageView)

我们查看一下Glide的部分源码:


  private static volatile Glide glide;
  //构造方法
  Glide(... ...) {
  	... ...
  }
  
  @NonNull
  public static Glide get(@NonNull Context context) {
    if (glide == null) {
      synchronized (Glide.class) {
        if (glide == null) {
          checkAndInitializeGlide(context);
        }
      }
    }

    return glide;
  }

其使用了双重检查的方法,来进行实现单例模式的。有个小不同的就是其构造方法,并没有用 private 修饰,但是也没有使用 public 修饰,这儿就表明了其封装的包内类是可以初始化 Glide 实例,而我们还是无法 new 一个实例。

四、小结

单例模式的使用场景还是比较多的,其实现也比较简单,本着学会一个用一个的想法,积极在项目中使用到,相信也是对整体代码起到了优化作用。不过一定要贴合具体需求,来看看自己是否需要使用该设计模式。

在 Android 里面,有两个地方会使得单例模式失效

  1. 多进程
  2. 反序列化

具体为什么,我们完全可以预想的到。多进程意味着有完全多个资源区,每个进程都会产生一个单例;而当我们序列化了一个单例类,产生了一个序列化的文件,我们可以通过该文件进行多次反序列化,也会产生多个相同的实例。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值