美文网首页
设计模式--单例模式

设计模式--单例模式

作者: 吃茶泡饭丶 | 来源:发表于2018-10-13 14:53 被阅读0次
    一点理解

    As we know,

    单例模式的意图:是为了确保某个类只有一个实例,并能自行实例化提供给外部使用。

    细想起来在Android中是怎么保证应用只有一个Application对象的呢?还有InputMethodManager...

    五种类型
    • 饿汉

    饿汉式就是自动加载,也就是在调用getInstance时已经产生实例,也就是在类加载时已经进行了实例化,天生的线程安全。
    这种模式的缺点就是占用资源,一般情况下我们都希望再用到的时候再去创建实例。
    这种方式适合占用资源少,在初始化时就被用到的类中。

    Java实现方式:

    /**
     * @Author: chichapaofan
     * @CreateDate: 2018/10/13
     * @Description:饿汉单例
     */
    public class Singleton_J2 {
    
        /**
         * 创建私有的实例,防止外部引用
         */
        private static Singleton_J2 singleton = new Singleton_J2();
    
        /**
         * 私有的构造方法,防止实例化
         */
        private Singleton_J2() {
    
        }
    
        /**
         * 提供对外方法,返回实例对象
         * @return
         */
        public static Singleton_J2 getInstance() {
            return singleton;
        }
    }
    

    Kotlin实现:

    /**
     * @Author: chichapaofam
     * @CreateDate: 2018/10/13
     * @Description:
     */
    class Singleton_K2 private constructor() {//私有的主构造器
    
        /**
         * 被companion object 包围的代码块都是static的
         */
        companion object {
            /**
             * 创建私有的实例,防止外部引用
             */
            private var singleton: Singleton_K2 = Singleton_K2()
    
            /**
             * 提供对外方法,返回实例对象
             * @return
             */
            fun getInstance(): Singleton_K2? {
                return singleton
            }
        }
    }
    

    相比于天生线程安全的饿汉,懒汉有很多的变种!!

    • 懒汉

    懒汉式就是延迟加载,也叫懒加载。在程序需要用到的时候再创建实例,这样保证了内存不会被浪费。
    缺点就是线程不安全,但这不是问题!

    Java实现方式:

    /**
     * @Author: chichapaofan
     * @CreateDate: 2018/10/13
     * @Description: 懒汉单例
     */
    public class Singleton_J1 {
    
        /**
         * 私有的实例防止外部引用
         */
        private static Singleton_J1 singleton = null;
    
        /**
         * 构造方法私有防止外部实例化
         */
        private Singleton_J1(){
    
        }
    
        /**
         * 唯一可以实例化的地方
         * @return
         */
        public static Singleton_J1 getInstance(){
            if(singleton==null){
                singleton=new Singleton_J1();
            }
            return singleton;
        }
    }
    

    kotlin实现方式:

    /**
     * @Author: chichapaofan
     * @CreateDate: 2018/10/13
     * @Description:
     */
    class Singleton_k1 private constructor() {//私有的主构造器
        /**
         * 被companion object包裹的语句都是static的
         */
        companion object {
            /**
             * 创建私有的null实例,防止外部引用
             */
            private var singleton: Singleton_k1? = null
    
            /**
             * 提供对外方法,返回实例对象
             * @return
             */
            fun getInstance(): Singleton_k1? {
                if (singleton == null) {
                    singleton = Singleton_k1()
                }
                return singleton
            }
        }
    }
    

    如何做到线程安全?当然是使用Synchronized关键字!

    普通变种:

    保证线程安全最简单的做法就是将getInstance()方法同步。
    同步方法虽然保证了线程安全,但是造成了不必要的同步开销。

    Java实现方式:

    public class Singleton_J1 {
    
        /**
         * 私有的实例防止外部引用
         */
        private static Singleton_J1 singleton = null;
    
        /**
         * 构造方法私有防止外部实例化
         */
        private Singleton_J1(){
    
        }
    
        /**
         * 唯一可以实例化的地方
         * @return
         */
        public static synchronized Singleton_J1 getInstance(){
            if(singleton==null){
                singleton=new Singleton_J1();
            }
            return singleton;
        }
    }
    

    Kotlin实现方式:

    class Singleton_k1 private constructor() {//私有的主构造器
        /**
         * 被companion object包裹的语句都是static的
         */
        companion object {
            private var singleton: Singleton_k1? = null
    
            @Synchronized fun getInstance(): Singleton_k1? {
                if (singleton == null) {
                    singleton = Singleton_k1()
                }
                return singleton
            }
        }
    }
    

    高级变种 Double Check Lock

    DCL双重检查锁机制(直白的翻译!)
    可以很好的解决懒加载单例模式的效率问题和线程安全问题。
    缺点:高并发条件下,由于JDK版本问题,在jdk1.5之前会失败。(几率很小)

    Java实现方式:

    /**
     * @Author: chichapaofan
     * @CreateDate: 2018/10/13
     * @Description:DCL
     */
    public class Singleton_J3 {
    
        private static Singleton_J3 singleton=null;
    
        private Singleton_J3(){
    
        }
    
        public static Singleton_J3 getInstance(){
            /**
             * 判断非空避免造成不必要的同步
             */
            if(singleton==null){
                /**
                 * 同步
                 */
                synchronized(Singleton_J3.class){
                    /**
                     * 判断在singleton为空情况下创建实例
                     */
                    if(singleton==null){
                        singleton=new Singleton_J3();
                    }
                }
            }
            return singleton;
        }
    }
    

    Kotlin实现方式:

    class Singleton_k3 private constructor() {
    
        companion object {
    
            private var singleton: Singleton_k3? = null
    
            fun getInstance(): Singleton_k3? {
                /**
                 * 判断非空避免造成不必要的同步
                 */
                if (singleton == null) {
                    /**
                     * 同步
                     */
                    synchronized(Singleton_k3) {
                        /**
                         * 判断在singleton为空情况下创建实例
                         */
                        if (singleton == null) {
                            singleton = Singleton_k3()
                        }
                    }
                }
                return singleton
            }
        }
    }
    
    • 静态内部类

    (饿汉模式的表亲)

    和饿汉模式一样,是靠JVM保证类的静态成员只能被加载一次的特点,从JVM层面保证了只会有一个实例对象。
    Excuse me?区别在哪?
    区别在于静态内部类,类加载时其静态内部类和非静态内部类不会同时被加载。。
    (一个类被加载,当且仅当其某个静态成员(静态域、构造器、静态方法等)被调用时发生)

    Java实现方式:

    /**
     * @Author: chichapaofan
     * @CreateDate: 2018/10/13
     * @Description:静态内部类单例
     */
    public class Singleton_J4 {
    
        /**
         * 私有构造方法防止被实例化
         */
        private Singleton_J4() {
    
        }
    
        /**
         * 提供外部实例方法
         * @return
         */
        public static Singleton_J4 getInstance() {
            return SingletonHolder.singleton;
        }
    
        /**
         * 私有的静态内部类
         */
        private static class SingletonHolder {
            private static Singleton_J4 singleton = new Singleton_J4();
        }
    }
    

    Kotlin实现方式:

    class Singleton_K4 private constructor(){//私有主构造器
    
        companion object {//static
    
            fun  getInstance():Singleton_K4{
                return SingletonHolder.singleton
            }
    
            private class SingletonHolder{
                companion object {
                    /**
                     * 在同一模块的任何地方可见
                     */
                    internal var singleton:Singleton_K4= Singleton_K4()
                }
            }
        }
    }
    

    加载singleton 类时不会初始化instance 只有在调用getInstance 方法时,才会导致instance 被初始化,这个方法不仅能确保线程安全,也能够保证单例对象的唯一性,同时也延迟了单例的实例化。优秀!优秀!!

    以上单例实现方法虽然能保证线程安全,但在序列化和反序列化的情况下会出现生成多个对象的情况

    怎么解决?Very simple!在单例类中加入readResolve()方法

    private Singleton_J3 readResolve(){
            return singleton;
    }
    
    /**
     *在静态内部类方式中这样使用
     */
     private Singleton_J4 readResolve(){
            return SingletonHolder.singleton;
     }
    

    当JVM从内存中反序列化地”组装”一个新对象时,就会自动调用这个 readResolve方法来返回我们指定好的对象了, 单例规则也就得到了保证。readResolve()的出现允许程序员自行控制通过反序列化得到的对象。

    • 枚举

    写法简单,线程简单,反序列化也不会重新创建对象,最重要的是默认枚举实例的创建是线程安全的。
    But:Android官方的Training课程中明确指出:
    Enums ofter require more than twice as much memory as static constants. You should strictly avoid using enums on Android.
    枚举通常需要的内存是静态常量的两倍多。你应该严格避免在Android上使用枚举。666

    Java实现方式:

    /**
     * @Author: chichapanfan
     * @CreateDate: 2018/10/13
     * @Description:枚举单例:声明一个枚举,用于获取数据库连接
     */
    public enum Singleton_J5 {
        /**
         *  定义一个枚举的元素,它就是Singleton的一个实例
         */
        INSTANCE;
    
        private DBConnection connection = null;
    
        Singleton_J5(){
            connection=new DBConnection();
        }
    
        public DBConnection doSomething(){
            return connection;
        }
    }
    

    如何使用枚举单例:

    Singleton_J5 singleton_j5 = Singleton_J5.INSTANCE.doSomething();
    

    需要注意的是单例如果持有Context对象,很容易引起内存泄漏,最好传递全局的Application Context。

    相关文章

      网友评论

          本文标题:设计模式--单例模式

          本文链接:https://www.haomeiwen.com/subject/kffoaftx.html