设计模式-单例模式

以最简单的module学习设计模式,理解最重要 连着更了几天,今天写个简单的,单例模式

前言

单例模式(Singleton Pattern)是 Java 中最简单的设计模式之一。

这种模式涉及到一个单一的类,该类负责创建自己的对象,同时确保只有单个对象被创建。这个类提供了一种访问其唯一的对象的方式,可以直接访问,不需要实例化该类的对象。

注意:

1、单例类只能有一个实例。 2、单例类必须自己创建自己的唯一实例。 3、单例类必须给所有其他对象提供这一实例。

正文:

注:由于单例模式是经常用到的,这里就不提供demo了(懒。。。) 本文就总结了Java中单例模式的几种实现方式,并比较了它们的优缺点

1. 最简单的实现---饿汉式

Java

/**
 * 饿汉式
 */public class Single {    private static Single single = new Single();    private Single() {    }    public static Single getInstance() {        return single;    }
}

写一个单例(不管什么形式),主要注意点如下几点(该种方式不存在线程安全的问题,其是线程安全的)

  1. 成员变量 single 要声明成静态的(static),因为需要在静态方法getInstance()中访问;
  2. 构造方法要声明成私有,不然如何保证单例;
  3. getInstance()要声明成 public static的。

Kotlin

/**
 * 饿汉式
 */object Single {
}

有童鞋要说了,这什么都没写呀。对,饿汉式在Kotlin中,只需要一个object修饰符就行了,这就是Kotlin非常厉害的地方。

2.性能优化(lazy load)——懒汉式

饿汉式的方式虽然简单,但是是基于classloader加载的,其在该类第一次加载进内存时就会初始化单例对象。这样,无论该对象是否被使用,都会创建一个single对象。

Java

/**
 * 懒汉式 --- 非线程安全
 */public class Single {    private static Single single;    private Single() {    }    public static Single getInstance() {        if (single == null) {            single = new Single();        }        return single;    }
}

Kotlin

/**
* 懒汉式 --- 非线程安全
*/class Single private constructor() {
  companion object {      /**
       * Kotlin原生写法
       */
      val INSTANCL_1 by lazy(LazyThreadSafetyMode.NONE) {
          Single()
      }      /**
       * 翻译java的写法
       */
      private var single: Single? = null      fun getInstance(): Single {          if (single == null) {              single = Single()          }          return single!!      }

  }
}

Kotlin这里有两种写法,一种是纯种,一种是变种。变种大家一看就明白,就是直接把Java的方式翻译过来了。纯种的我们使用了lazy,看英文就知道是懒加载的方式,传入了一个LazyThreadSafetyMode.NONE,英文好的小伙伴一看就明白,这是线程不安全的意思。companion object的意思相当于Java中public static。

3. 懒汉式——线程安全(1)

因为懒汉式的出现,虽然解决了饿汉式的不足,但也出现了多线程的问题。于是解决懒汉式的方式就出现了,那就是我们熟知的加锁Synchronized。 Java

/**
 * 懒汉式 --- 线程安全
 * 使用synchronized保证线程安全
 * 虽然线程安全了,但因为使用synchronized关键字使加锁效率不高 */public class Single {    private static Single instance;    private Single() {    }    public static synchronized Single getInstance() {        if (instance == null) {            instance = new Single();        }        return instance;    }
}

Kotlin

/**
 * 懒汉式 --- 线程安全
 * 使用synchronized保证线程安全
 * 虽然线程安全了,但因为使用synchronized关键字使加锁效率不高
 * Kotlin使用@Synchronized注解加锁
 */class Single private constructor() {    companion object {        private var single: Single? = null
        @Synchronized        fun getInstance(): Single {            if (single == null) {                single = Single()            }            return single!!        }
    }

}

4. 懒汉式——线程安全(2):效率更高

Java

/**
 * 懒汉式 --- 线程安全 双重校验锁 */public class Single {    private static Single single;   private Single() {    }    public static Single getInstance() {        if (single == null) {            synchronized (Single.class) {                if (single == null) {                    single = new Single();
                }
            }        }        return single;    }

}

但是上面的单例都有其缺陷:当反序列化和使用java的反射机制时,单例无法得到保证,那么,解决该问题,我们可以使用Enum(枚举)。 Kotlin

/**
 * 懒汉式 --- 线程安全 双重校验锁
 */class Single private constructor() {

    companion object {        /**
         * Kotlin原生写法
         */
        val INSTANCE_1 by lazy(mode = LazyThreadSafetyMode.SYNCHRONIZED) {
            Single()
        }        /**
         * 翻译java写法(变种方法)
         */
       private var single: Single? = null        fun getInstance(): Single {            if (single == null) {                synchronized(Single::class.java) {                    if (single == null) {                        single = Single()
                    }
                }            }            return single!!        }
    }

}

Kotlin原生的,我们只改变了lazy的括号的值,mode = LazyThreadSafetyMode.SYNCHRONIZED就是锁的意思,英文好的童鞋一眼就明白了。

5. 枚举实现

这种方式是Effective Java作者Josh Bloch 提倡的方式,它不仅能避免多线程同步问题,而且还能防止反序列化和反射机制重新创建新的对象,不过,JDK1.5中才加入enum特性, 这种方式只能在1.5之后使用。(但是Android官网不建议使用enums,占用内存多(Enums often require more than twice as much memory as static constants.)) Java

/**
 * 枚举实现
 */public enum Single {    INSTANCE;    public void method() {    }
}

Kotlin

/**
 * 枚举实现
 */public enum Single {    INSTANCE;    public void method() {    }
}

6.内部类式

虽然不怎么被大家使用,但是觉得还是比较好。还是要说一下的 Java

/**
 * 内部类实现
 */public class Single {
    private Single() {    }    private static class Hodler {        private static Single single = new Single();    }    public static Single getInstance() {        return Hodler.single;    }
}

内部类Holder,里面有外部的实例。很多童鞋可能要问,这怎么就满足懒汉式和线程安全呢?当我们应用初始化时,getInstance没有被调用,就没有实例对象,那就满足了懒汉式。当我们调用getInstance的时候,Java虚拟机为了保证类加载的安全性,所以这里就保证了线程安全。这种写法是不是惊呆了?那Kotlin又是怎么样写的呢?

/**
 * 内部类实现
 */class Single private constructor() {    companion object {        fun getInstance(): Single {            return Hodler.single        }    }    private object Hodler {        val single = Single()
    }
}

很简单,内部用object创建一个内部Holder单例,外部一个getInstance来获取的方法。也相当于是Java翻译过来的方式。

设计模式持续更新中:https://www.jianshu.com/p/e3c25095c31f

原文发布于微信公众号 - Android历练记(gh_db8538619cdd)

原文发表时间:2018-06-08

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏王硕

原 PostgreSQL的系统函数分析记录

1473
来自专栏博客园

Core官方DI解析(4)--CallSiteRuntimeResolver

这两个类都在其CallSiteVisitor<TArgument, TResult>基类中

913
来自专栏数据结构与算法

cf580E. Kefa and Watch(线段树维护字符串hash)

首先有个神仙结论:若询问区间为$(l, r, d)$,则只需判断$(l + d, r)$和$(l, r - d )$是否相同

1231
来自专栏崔庆才的专栏

Python操作Redis,你要的都在这了!

8.1K4
来自专栏技术小讲堂

iBatis.Net(6):Data Map(深入)

在上一篇中,我写了几个最最基本的DataMap映射,但是如果仅仅是这些功能的话,那iBatis真就有点愧对它的粉丝啦,我个人的理解,iBatis真的可以让开发者...

3059
来自专栏从零开始学 Web 前端

嵌入式经典面试题

一个定义为volatile的变量是说这变量可能会被意想不到地改变,这样,编译器就不会去假设这个变量的值了。精确地说就是,优化器在用到这个变量时必须每次都小心地重...

3523
来自专栏Python、Flask、Django

Python partition使用技巧

902
来自专栏Jackson0714

PHP内核之旅-3.变量

3036
来自专栏小小挖掘机

来学习几个简单的Hive函数啦

咳咳,今天来介绍一下几个Hive函数吧,先放一张我登哥划水的照片,希望大家也做一只自由的鱼儿,在知识的海洋里游呀游,嘻嘻! ? 今天我们来介绍几个Hive常用的...

4464
来自专栏Java开发

FastJson过滤字段

1、在对象对应字段前面加transient,表示该字段不用序列化,即在生成json的时候就不会包含该字段了。 比如

3052

扫码关注云+社区

领取腾讯云代金券