上一次被人说文章名字取得不霸气,于是这一次我采用了这么霸气的名字,但实际上我是一个很低调的人。设计模式刚入门的小伙伴可以先看看这篇《设计模式入门》,在文章末尾也将列出“设计模式系列”文章。欢迎大家关注留言投币丢香蕉。
单例模式有三个要点,分别是:
这也就是我们在写单例模式时候必须要遵守的几点。根据上面的条件,我们能够得出:
懒汉式这名字的精髓在于懒,在代码中,这种懒代表着需要的时候才创建实例,不需要就不创建了,这样保证了资源的不浪费。
public class JavaSingleton {
private JavaSingleton(){}//私有构造方法
private static JavaSingleton instance;//提供一个实例对象
public static JavaSingleton getInstance(){//对外提供可获取唯一实例化对象的方法
if(instance == null)
instance = new JavaSingleton(); //延迟实例化
return instance;
}
}
复制代码
class KotlinSingleton {
private var instance : KotlinSingleton? = null
fun getInstance(): KotlinSingleton? {
if(instance == null)
instance = KotlinSingleton()
return instance
}
}
复制代码
class KotlinSingleton {
companion object {
val instance by lazy(LazyThreadSafetyMode.NONE) {
KotlinSingleton()
}
}
}
// 在Kotlin 中调用
KotlinSingleton.instance.xx()
// 在Java 中调用
KotlinSingleton.Companion.getInstance().xx()
复制代码
kotlin方式1就是直译了java方式,没啥说的。kotlin方式2写着很简洁,也很明了。要提一点的就是companion object,他类似public static,这一点从下面调用方式也看得出来。lazy属性表明是懒加载方式,LazyThreadSafetyMode.NONE表明了这种方式,线程不安全。
我们已经按照单例模式三个要点写出了单例模式,其实都已经讲完了。但为何还有其他一些写法呢? 因为我们是程序员啊!优化!优化!优化! 单线程进程下,懒汉式完全够用。但实际开发哪还有什么单线程程序。既然存在多线程,就存在并发性,如果两个线程同时进入非空判断,问题就出现了。线程一可能创建了一个instance,线程二因为已经非空判断为空了,所以也创建了个instance。这样程序肯定不会朝着你预想的方向去。 所以我们可以给非空判断加一把锁,防止多线程同时进入非空判断。
public class JavaSingleton {
private JavaSingleton(){}//私有构造方法
private static JavaSingleton instance;//提供一个实例对象
public static synchronized JavaSingleton getInstance(){//对外提供可获取唯一实例化对象的方法
if(instance == null)
instance = new JavaSingleton(); //延迟实例化
return instance;
}
}
复制代码
class KotlinSingleton {
private var instance : KotlinSingleton? = null
@Synchronized //用注解就okk了。
fun getInstance(): KotlinSingleton? {
if(instance == null)
instance = KotlinSingleton()
return instance
}
}
复制代码
简单!粗暴!太粗暴了!Synchronized 是一把重型锁,对性能影响相当的大,像单例这样在软件中可能多次获取,那性能将会大大地降低!所以,这种方式不建议使用。
为了解决多线程的问题,又不想降低性能,“饿汉式”就来了。饿汉式名字精髓在于“饿”,迫切的想吃东西,在代码中表示,迫切的想有一个单例对象!即在初始化的时候,就把单例创建好,之后要用就直接return,但是不用就浪费了。这也是饿汉式的缺点。
public class JavaSingleton {
private JavaSingleton(){}//私有构造方法
private static JavaSingleton instance = new JavaSingleton();//提供一个实例对象
public static JavaSingleton getInstance(){//对外提供可获取唯一实例化对象的方法
return instance;
}
}
复制代码
object KotlinSingleton {
//null
}
// 在Kotlin 中调用
KotlinSingleton.xx()
// 在Java 中调用
KotlinSingleton.INSTANCE.xx()
复制代码
酥糊!Kotlin!有牌面!
前面讲的三种方式都有不同程度的缺点。而双重检查锁,既保证了延迟加载不浪费资源,又保证了较好的性能。但双重检查锁不适用于1.4以及更早版本的java。
public class JavaSingleton {
private JavaSingleton(){}
//volatile确保了当instance初始化为JavaSingleton实例时,多个线程正确的处理instance变量。
private volatile static JavaSingleton instance;
public static JavaSingleton getInstance(){
if(instance == null){
synchronized (JavaSingleton.class){//只有第一次才彻底执行锁块代码
if(instance == null){
instance = new JavaSingleton();
}
}
}
return instance;
}
}
复制代码
class KotlinSingleton {
@Volatile //用注解就okk了
private var instance : KotlinSingleton? = null
fun getInstance(): KotlinSingleton? {
if(instance == null){
synchronized(KotlinSingleton::class){
if(instance == null){
instance = KotlinSingleton()
}
}
}
return instance
}
}
复制代码
class KotlinSingleton {
companion object {
val instance by lazy (mode = LazyThreadSafetyMode.SYNCHRONIZED){
KotlinSingleton()
}
}
}
// 在Kotlin 中调用
KotlinSingleton.instance.xx()
// 在Java 中调用
KotlinSingleton.Companion.getInstance().xx()
复制代码
“双重检查锁”的没有明显的缺点,如果非要说一个,可能就是太复杂了。kotlin还好,java里面写着相当的复杂。如果程序对性能没有考虑的话,这样写显然就太麻烦了。
关于LazyThreadSafetyMode
延迟属性 Lazy 可选LazyThreadSafetyMode三种模式:
不要私信问我店长是谁! 总之这种方式,解决了延迟加载,线程安全的问题,还代码量少!简直美滋滋!但跟之前不同的是,没有声明实例对象。
public class JavaSingleton {
private JavaSingleton(){}
private static class Holder{
private static JavaSingleton instance = new JavaSingleton();
}
public static JavaSingleton getInstance(){
return Holder.instance;
}
}
复制代码
class KotlinSingleton {
companion object {
fun getInstance() = Holder.instance
}
private object Holder{
val instance = KotlinSingleton()
}
}
复制代码
在类加载时,因为没有调用getInstance()所以Holder也不会加载,这样就实现了懒加载。调用getInstance()时,JVM会主动保证类加载的安全性,所以线程也是安全的。kotlin的写法就是java的翻译版本。
上面几种方式都是比较官方的版本,下面介绍几个民间版本,是真大神还是抖机灵,自行判断。我也去研究研究!
来自知乎@丌冰: “我觉得这样就好了,什么懒加载,双重什么什么,什么什么的”
fun main(args: Array<String>) {
Instance.INSTANCE.fun1()
print(Instance.INSTANCE.fun2())
}
enum class Instance {
INSTANCE;
fun fun1(){
}
fun fun2():Any?{
return null
}
}
作者:丌冰
链接:https://www.zhihu.com/question/52377186/answer/303561470
来源:知乎
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
复制代码
可见Android官方并不推荐使用枚举,占用内存较多。
来自 刘望舒 的 设计模式(二)单例模式的七种写法 :
public class SingletonManager {
private static Map<String, Object> objMap = new HashMap<String,Object>();
private Singleton() {
}
public static void registerService(String key, Objectinstance) {
if (!objMap.containsKey(key) ) {
objMap.put(key, instance) ;
}
}
public static ObjectgetService(String key) {
return objMap.get(key) ;
}
}
复制代码
这也是比较少见的单例写法,将多个单例放在进SingletonManager 的静态map中统一管理。我是觉得有点太过于复杂,容易出错。
以上版本仅供参考,要是用了对程序造成了什么不好的影响,别找我。
关于继承:不能继承!别想了。
关于单例:尽量少使用单例。
关于我还没想到的问题:欢迎加群讨论557247785。