草庐IT

kotlin 之单例类详解

易帜¤ 2023-05-26 原文

object

单例对象的声明:

object  Model{
    var temp = "1"
    val temp2 = "2"
    const val temp3 = "3"
}

抛出疑问:使用object修饰的类,是哪种类型的单例模式

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

1. 饿汉式
public class HungryMan {

    private HungryMan(){}

    private static HungryMan hungryMan = new HungryMan();

    public static HungryMan getInstance(){
        return hungryMan;
    }
}

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

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

2. 懒汉式
public class LazyMan {

    private static LazyMan lazyMan = null;

    private LazyMan() {
    }

    public static LazyMan getInstatce() {
        if (lazyMan == null) {
            lazyMan = new LazyMan();
        }
        return lazyMan;
    }
}

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

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

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;
    }
}

优点:支持多线程

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

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;
    }
}

这里引入一下解释一下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;
    }
}

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

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

6. 枚举单例
public enum SingleTon {

    SINGLE_TON;

    private String field;

    public String getField() {
        return field;
    }

    public void setField(String field) {
        this.field = field;
    }
}

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

缺点:枚举类占用内存多

解析: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() {
      
    }
}

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();
      }
   }
}

可以看的出来,如果直接对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();
      }
   }
}

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

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

有关kotlin 之单例类详解的更多相关文章

  1. 物联网MQTT协议详解 - 2

    一、什么是MQTT协议MessageQueuingTelemetryTransport:消息队列遥测传输协议。是一种基于客户端-服务端的发布/订阅模式。与HTTP一样,基于TCP/IP协议之上的通讯协议,提供有序、无损、双向连接,由IBM(蓝色巨人)发布。原理:(1)MQTT协议身份和消息格式有三种身份:发布者(Publish)、代理(Broker)(服务器)、订阅者(Subscribe)。其中,消息的发布者和订阅者都是客户端,消息代理是服务器,消息发布者可以同时是订阅者。MQTT传输的消息分为:主题(Topic)和负载(payload)两部分Topic,可以理解为消息的类型,订阅者订阅(Su

  2. Tcl脚本入门笔记详解(一) - 2

    TCL脚本语言简介•TCL(ToolCommandLanguage)是一种解释执行的脚本语言(ScriptingLanguage),它提供了通用的编程能力:支持变量、过程和控制结构;同时TCL还拥有一个功能强大的固有的核心命令集。TCL经常被用于快速原型开发,脚本编程,GUI和测试等方面。•实际上包含了两个部分:一个语言和一个库。首先,Tcl是一种简单的脚本语言,主要使用于发布命令给一些互交程序如文本编辑器、调试器和shell。由于TCL的解释器是用C\C++语言的过程库实现的,因此在某种意义上我们又可以把TCL看作C库,这个库中有丰富的用于扩展TCL命令的C\C++过程和函数,所以,Tcl是

  3. 【详解】Docker安装Elasticsearch7.16.1集群 - 2

    开门见山|拉取镜像dockerpullelasticsearch:7.16.1|配置存放的目录#存放配置文件的文件夹mkdir-p/opt/docker/elasticsearch/node-1/config#存放数据的文件夹mkdir-p/opt/docker/elasticsearch/node-1/data#存放运行日志的文件夹mkdir-p/opt/docker/elasticsearch/node-1/log#存放IK分词插件的文件夹mkdir-p/opt/docker/elasticsearch/node-1/plugins若你使用了moba,直接右键新建即可如上图所示依次类推创建

  4. 【Elasticsearch基础】Elasticsearch索引、文档以及映射操作详解 - 2

    文章目录概念索引相关操作创建索引更新副本查看索引删除索引索引的打开与关闭收缩索引索引别名查询索引别名文档相关操作新建文档查询文档更新文档删除文档映射相关操作查询文档映射创建静态映射创建索引并添加映射概念es中有三个概念要清楚,分别为索引、映射和文档(不用死记硬背,大概有个印象就可以)索引可理解为MySQL数据库;映射可理解为MySQL的表结构;文档可理解为MySQL表中的每行数据静态映射和动态映射上面已经介绍了,映射可理解为MySQL的表结构,在MySQL中,向表中插入数据是需要先创建表结构的;但在es中不必这样,可以直接插入文档,es可以根据插入的文档(数据),动态的创建映射(表结构),这就

  5. 最强Http缓存策略之强缓存和协商缓存的详解与应用实例 - 2

    HTTP缓存是指浏览器或者代理服务器将已经请求过的资源保存到本地,以便下次请求时能够直接从缓存中获取资源,从而减少网络请求次数,提高网页的加载速度和用户体验。缓存分为强缓存和协商缓存两种模式。一.强缓存强缓存是指浏览器直接从本地缓存中获取资源,而不需要向web服务器发出网络请求。这是因为浏览器在第一次请求资源时,服务器会在响应头中添加相关缓存的响应头,以表明该资源的缓存策略。常见的强缓存响应头如下所述:Cache-ControlCache-Control响应头是用于控制强制缓存和协商缓存的缓存策略。该响应头中的指令如下:max-age:指定该资源在本地缓存的最长有效时间,以秒为单位。例如:Ca

  6. IDEA 2022 创建 Spring Boot 项目详解 - 2

    如何用IDEA2022创建并初始化一个SpringBoot项目?目录如何用IDEA2022创建并初始化一个SpringBoot项目?0. 环境说明1.  创建SpringBoot项目 2.编写初始化代码0. 环境说明IDEA2022.3.1JDK1.8SpringBoot1.  创建SpringBoot项目        打开IDEA,选择NewProject创建项目。        填写项目名称、项目构建方式、jdk版本,按需要修改项目文件路径等信息。        选择springboot版本以及需要的包,此处只选择了springweb。        此处需特别注意,若你使用的是jdk1

  7. ruby - 为什么我可以使用像 `puts` 这样的内核单例方法? - 2

    在Ruby中,方法puts是Kernel的单例方法模块。通常,当一个模块是included或extend由另一个模块编辑,该模块(但不是它的单例类)被添加到继承树中。这有效地使模块的实例方法可用于模块或其单例类(分别用于include和extend)......但混合模块的单例方法仍然无法访问,因为单例类从未将模块添加到继承树中。那么为什么我可以使用puts(和其他内核单例方法)?Kernel.singleton_methods(false)#=>[:caller_locations,:local_variables,:require,:require_relative,:autolo

  8. ruby - 单例类和实例变量 - 2

    为什么instance_variables方法不显示针对变量a的@var_one?a=Object.newdefa.my_eval;yieldenda.my_eval{@var_one=1}a.instance_variables#=>[]instance_variables#=>[@var_one] 最佳答案 你应该使用instance_eval:a.instance_eval{@var_one=1}=>1a.instance_variables=>[:@var_one]当你使用普通的eval时,你在当前self的上下文中定义你的

  9. 详解Unity中的粒子系统Particle System (二) - 2

    前言上一篇我们简要讲述了粒子系统是什么,如何添加,以及基本模块的介绍,以及对于曲线和颜色编辑器的讲解。从本篇开始,我们将按照模块结构讲解下去,本篇主要讲粒子系统的主模块,该模块主要是控制粒子的初始状态和全局属性的,以下是关于该模块的介绍,请大家指正。目录前言本系列提要一、粒子系统主模块1.阅读前注意事项2.参考图3.参数讲解DurationLoopingPrewarmStartDelayStartLifetimeStartSpeed3DStartSizeStartSize3DStartRotationStartRotationFlipRotationStartColorGravityModif

  10. VMware虚拟机与本地主机进行磁盘共享(详解) - 2

    VMware虚拟机与本地主机进行磁盘共享前提虚拟机版本为Windows10(专业版,不是可能有问题)本地主机为家庭版或学生版(此版本会有问题,但有替代方式)最好是专业版VMware操作1.关闭防火墙,全部关闭。2.打开电脑属性3.点击共享-》高级共享-》权限4.如果没有everyone,就添加权限选择完全控制,然后应用确定。5.打开cmd输入lusrmgr.msc(只有专业版可以打开)如果不是专业版,可以跳过这一步。点击用户-》administrator密码要复杂密码,否则不行。推荐admaiN@1234类型的密码。设置完密码,点击属性,将禁用解开。6.如果虚拟机的windows不是专业版,可

随机推荐