• kotlin 之单例类详解


    object

    单例对象的声明:

    object  Model{
        var temp = "1"
        val temp2 = "2"
        const val temp3 = "3"
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    抛出疑问:使用object修饰的类,是哪种类型的单例模式

    这里我们先回顾一下java六种单例模式

    1. 饿汉式
    public class HungryMan {
    
        private HungryMan(){}
    
        private static HungryMan hungryMan = new HungryMan();
    
        public static HungryMan getInstance(){
            return hungryMan;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    优点:简单方便,线程安全

    缺点:无论是否用到,都会进行实例化,而且在类加载时就会实例化

    2. 懒汉式
    public class LazyMan {
    
        private static LazyMan lazyMan = null;
    
        private LazyMan() {
        }
    
        public static LazyMan getInstatce() {
            if (lazyMan == null) {
                lazyMan = new LazyMan();
            }
            return lazyMan;
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    优点:只有在使用时才会生成对象,能够减少内存开销

    缺点:线程不安全,只能在单线程中使用,多个线程访问时,会产生多个对象,

    3.懒汉式同步锁
    public class LazyMan {
    
        private static volatile LazyMan lazyMan = null;
    
        private LazyMan() {
        }
    
        public static LazyMan getInstatce() {
            synchronized (LazyMan.class){
                if (lazyMan == null) {
                    lazyMan = new LazyMan();
                }
            }
            return lazyMan;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    优点:支持多线程

    缺点:每次都会有一个加锁以及释放锁的操作,效率低,可以通过反射破坏单例模式。

    4.DCL双检测锁
    public class LazyMan {
    
        private static volatile LazyMan lazyMan = null;
    
        private LazyMan() {
        }
    
        public static LazyMan getInstatce() {
            if(lazyMan == null){
                synchronized (LazyMan.class){
                    if (lazyMan == null) {
                        lazyMan = new LazyMan();
                    }
                }
            }
            return lazyMan;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    这里引入一下解释一下DCL双检测锁机制:

    DCL双检测锁机制: 用DCL双检测锁机制为什么要用valoatile修饰,因为lazyMan=new LazyMan(), 并非是一个原子操作。事实上在JVM中大概做了3件事。

    1.给lazyMan分配内存,

    2.调用构造器来初始化成员变量

    3.将lazyMan对象指向分配的内存空间。 但是JVM的即时编译器中存在指令重排序的优化,也就是说上面的第二步,第三步顺序是不 确定的一旦2,3,顺序乱了,这个是有一个线程调用了方法,结果虽然是非null,但是未初 始化,所以直接报错。

    优点:效率高,线程安全

    缺点:代码复杂,可以通过反射破坏单例

    5.静态内部类
    public class Singleton {
     
        private Singleton() {}
     
        private static class SingletonInstance {//私有静态内部类
            private static final Singleton INSTANCE = new Singleton();
        }
     
        public static Singleton getInstance() {
            return SingletonInstance.INSTANCE;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    优点:类的静态属性只有在第一次加载类的时候初始化,所以线程安全

    缺点:代码变得复杂,apk文件增大

    6. 枚举单例
    public enum SingleTon {
    
        SINGLE_TON;
    
        private String field;
    
        public String getField() {
            return field;
        }
    
        public void setField(String field) {
            this.field = field;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    优点:线程安全,不用担心反射破话单例模式

    缺点:枚举类占用内存多

    解析:object 单例类是什么类型的单例

    这里我们直接将kotlin代码转为Java 代码进行查看。

    kotlin代码如下

    转为Java之后

    我们可以看到,该Model类转为Java代码之后,它是一个饿汉式单例。所以使用object的类采用的是饿汉式单例。

    companion object伴生对象出现的单例是哪种类型的单例

    kotlin代码如下

    class  Model{
        companion object{
            val text = ApiWrapper("11")
        }
    }
    
    class ApiWrapper (val api : String){
         fun s() {
          
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    java代码如下

    public final class Model {
       @NotNull
       private static final ApiWrapper text = new ApiWrapper("11");
       @NotNull
       public static final Model.Companion Companion = new Model.Companion((DefaultConstructorMarker)null);
    
       public static final class Companion {
          @NotNull
          public final ApiWrapper getText() {
             return Model.text;
          }
    
          private Companion() {
          }
          public Companion(DefaultConstructorMarker $constructor_marker) {
             this();
          }
       }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    可以看的出来,如果直接对text赋值,那么就相当于是一个饿汉式加载

    但是如果我们对text进行by lazy延迟赋值,那么会是什么样子呢。

    public final class Model {
       @NotNull
       private static final Lazy text$delegate;
       @NotNull
       public static final Model.Companion Companion = new Model.Companion((DefaultConstructorMarker)null);
    
       static {
          text$delegate = LazyKt.lazy((Function0)null.INSTANCE);
       }
    
       public static final class Companion {
          @NotNull
          public final ApiWrapper getText() {
             Lazy var1 = Model.text$delegate;
             Model.Companion var2 = Model.Companion;
             Object var3 = null;
             return (ApiWrapper)var1.getValue();
          }
    
          private Companion() {
          }
    
          public Companion(DefaultConstructorMarker $constructor_marker) {
             this();
          }
       }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27

    可以看出,此时变成了懒汉式同步单例

    至于为什么是同步单例,这里需要大家去看一下LazyKt.lazy()方法

  • 相关阅读:
    怎么找到没有广告的安全浏览器?这款干净实用
    适合小孩接触编程起步的几款软件,从游戏中学习编程
    图扑软件数字孪生民航飞联网,构建智慧民航新业态
    解决WPF项目xaml出现“正在等待IntelliSense完成”的问题
    什么是策划能力?如何提高策划能力?
    【vue-upload】表单中自定义头像上传,或手动上传
    2023Web前端面试题及答案(一)
    C++内存泄漏排查以及几个工具
    七天学会C语言-第二天(数据结构)
    flume框架原理
  • 原文地址:https://blog.csdn.net/weixin_44710164/article/details/127889022