Kotlin(二) 单例的加载

由于项目实战的时候会使用一些kotlin的关键字,为了项目的提前准备,我们先做点知识储备.

前言 伴生对象

kotlin中没有直接的静态方法的概念,但可以通过使用 companion object 关键字来模拟静态方法的行为.

kotlin 复制代码
class MyClass{
    companion object{
        fun staticMethod(){
            //静态方法的实现
            println("this is staticMethod")
        }
    }

    fun test(){
        println("test method")
    }
}

fun main() {
    MyClass.staticMethod()
}
kotlin 复制代码
this is staticMethod

类似java的静态方法调用,可以直接通过类名调用,而不需要创建类的实例.

Kotlin的静态方法可以用于工具类中算法的实现、单例模式等.

含义

伴生对象是与类一起诞生的对象,类一加载,它的伴生对象也就被创建了。

每个类都可以对应一个伴生对象,并且该伴生对象的成员全局独一份。

kotlin 复制代码
class Rectangle(val width:Int,val height:Int) {



    companion object{
        fun ofSize(width: Int,height: Int): Rectangle{
            return Rectangle(width,height)
        }

        fun ofRectangle(rectangle: Rectangle): Rectangle{
            return Rectangle(rectangle.width,rectangle.height)
        }
    }

}

fun main() {
    val width=4
    val height=5
    val rectangle1= Rectangle(width,height)//调用构造方法
    val rectangle2= Rectangle.ofRectangle(rectangle1)//通过类名调用方法
    val rectangle3= Rectangle.ofSize(width,height) //通过类名调用方法
}

借助 Show Kotlin Bytecode工具,将上述Kotlin代码反编译成java代码如下:

java 复制代码
public final class Rectangle {
   ...
   public static final Rectangle.Companion Companion = new Rectangle.Companion((DefaultConstructorMarker)null);

   public static final class Companion {
      @NotNull
      public final Rectangle ofSize(int width, int height) {
         return new Rectangle(width, height);
      }

      @NotNull
      public final Rectangle ofRectangle(@NotNull Rectangle rectangle) {
         Intrinsics.checkParameterIsNotNull(rectangle, "rectangle");
         return new Rectangle(rectangle.getWidth(), rectangle.getHeight());
      }

      private Companion() {
      }

      // $FF: synthetic method
      public Companion(DefaultConstructorMarker $constructor_marker) {
         this();
      }
   }
}

可以看到,Kotlin中的companion object只是Rectangle的一个静态内部类实例

JVM静态成员(@JvmStatic)

此时如果我们在java中重新初始化Rectangle

arduino 复制代码
public class JavaTest1 {

    public static void main(String[] args) {
        int width=4;
        int height=5;
        Rectangle rectangle1=new Rectangle(width,height);
        Rectangle rectangle2=Rectangle.Companion.ofSize(width,height);
        Rectangle rectangle3=Rectangle.Companion.ofRectangle(rectangle2);
    }
}

由于Kotlin中Rectangle的伴生对象本质上就是Rectangle.Companion实例.Java如何像Kotlin直接通过类名.xxx()的方法来调用伴生对象方法.

Kotlin提供了@JvmStatic和@JvmField

kotlin 复制代码
class Rectangle(val width:Int,val height:Int) {

    init {
        println(width)
        println(height)
    }

    companion object{
        
        @JvmField
        val Tag="Rectangle"
        
        @JvmStatic
        fun ofSize(width: Int,height: Int): Rectangle{
            return Rectangle(width,height)
        }
        
        @JvmStatic
        fun ofRectangle(rectangle: Rectangle): Rectangle{
            return Rectangle(rectangle.width,rectangle.height)
        }
    }

}

此时我们也可以像kotlin中一样直接调用ofSize和ofRectangle方法初始化Rectangle

ini 复制代码
public class JavaTest1 {

    public static void main(String[] args) {
        int width=4;
        int height=5;
        Rectangle rectangle1=new Rectangle(width,height);
        Rectangle rectangle2=Rectangle.Companion.ofSize(width,height);
        Rectangle rectangle3=Rectangle.Companion.ofRectangle(rectangle2);

        Rectangle rectangle4=Rectangle.ofSize(width,height);
        Rectangle rectangle5=Rectangle.ofRectangle(rectangle2);
    }
}

反编译后

在Rectangle中增加了对应的static属性和方法

less 复制代码
public final class Rectangle {
   public static final Rectangle.Companion Companion = new Rectangle.Companion((DefaultConstructorMarker)null);

   @JvmField
   @NotNull
   public static final String Tag = "Rectangle";

   @JvmStatic
   @NotNull
   public static final Rectangle ofSize(int width, int height) {
      return Companion.ofSize(width, height);
   }

   @JvmStatic
   @NotNull
   public static final Rectangle ofRectangle(@NotNull Rectangle rectangle) {
      return Companion.ofRectangle(rectangle);
   }

   public static final class Companion {
      @JvmStatic
      @NotNull
      public final Rectangle ofSize(int width, int height) {
          ...
      }

      @JvmStatic
      @NotNull
      public final Rectangle ofRectangle(@NotNull Rectangle rectangle) {
          ...
      }
   }
}

一、饿汉式

首先用java实现一个单例

csharp 复制代码
public class SingletonDemo {
    
    private static SingletonDemo instance =new SingletonDemo();
    
    private SingletonDemo(){
        
    }
    
    public static SingletonDemo getInstance(){
        return instance;
    }
}
kotlin 复制代码
object SingletonDemoTest {
    fun test() {
        println("test1 fun")
    }

    fun test2() {
        println("test2 fun")
    }
}

fun main() {
    SingletonDemoTest.test()
    SingletonDemoTest.test2()
}

仅仅通过object的对象声明就得到了kotlin的一个单例. 通过as的字节码插件我们看下kotlin到底做了哪些工作.

查看Kotlin对应字节码

此时再点击字节码左上侧的Decompile按钮

java 复制代码
public final class SingletonDemoTest {
   @NotNull
   public static final SingletonDemoTest INSTANCE = new SingletonDemoTest();

   private SingletonDemoTest() {
   }

   public final void test() {
      String var1 = "test1 fun";
      System.out.println(var1);
   }

   public final void test2() {
      String var1 = "test2 fun";
      System.out.println(var1);
   }
}

可以看到虽然是一个object的声明,但是底层仍是帮我们声明了饿汉式的单例声明

二、懒汉式(线程不安全)

csharp 复制代码
public class SingletonDemo2 {
    private static SingletonDemo2 instance;
    private SingletonDemo2(){}

    public static SingletonDemo2 getInstance(){
        if(instance==null){
            instance=new SingletonDemo2();
        }
        return instance;
    }
}

懒汉式和饿汉式的区别是当首次获取getInstance实例时会进行一次判空,值得提出的是,上面的懒汉式是线程不安全的.

kotlin 复制代码
class SingletonDemoTest2 private constructor(){

    companion object{
        private var instance: SingletonDemoTest2?=null
            get() {
                if (field == null) {
                    field = SingletonDemoTest2()
                }
                return field
            }

        fun get(): SingletonDemoTest2{
            return instance!!
        }
    }
    fun test() {
        println("test1 fun")
    }

    fun test2() {
        println("test2 fun")
    }
}

fun main() {
    SingletonDemoTest2.get().test()
    SingletonDemoTest2.get().test2()
}

1、在伴生对象中私有构造方法getter()对单例对象进行赋值.

2、通过instance2!!触发getter逻辑

三、懒汉式(线程安全)

由于上面的代码在多线程并发的时候有线程安全的问题,所以我们尝试在实例化的时候加入synchronized同步锁

csharp 复制代码
public class SingletonDemo2 {
    private static SingletonDemo2 instance;
    private SingletonDemo2(){}

    public static synchronized SingletonDemo2 getInstance(){
        if(instance==null){
            instance=new SingletonDemo2();
        }
        return instance;
    }
}
kotlin 复制代码
class SingletonDemoTest2 private constructor(){

    companion object{
        private var instance: SingletonDemoTest2?=null
            get() {
                if (field == null) {
                    field = SingletonDemoTest2()
                }
                return field
            }
        
        @Synchronized
        fun get(): SingletonDemoTest2{
            return instance!!
        }
    }
    fun test() {
        println("test1 fun")
    }

    fun test2() {
        println("test2 fun")
    }
}

fun main() {
    SingletonDemoTest2.get().test()
    SingletonDemoTest2.get().test2()
}

Kotlin相应地在方法上加上@Synchronized注解即可.

四、双重校验锁式(Double Check Lock)

csharp 复制代码
public class SingletonDemo3 {
    private volatile static SingletonDemo3 instance;
    private SingletonDemo3(){}

    public static SingletonDemo3 getInstance(){
        if(instance==null){
            synchronized (SingletonDemo3.class){
                if(instance==null){
                    instance=new SingletonDemo3();
                }
            }
        }
        return  instance;
    }
}

这是java的实现方式,没有给实例化的整个方法上锁,而是将synchronized锁在了几行代码内,同时使用volatile保证了可见性.

kotlin的实现就相对比较简洁了.

kotlin 复制代码
class Singleton3 private constructor() {
    companion object {
        val instance: Singleton3 by lazy(mode = LazyThreadSafetyMode.SYNCHRONIZED) {
            Singleton3()
        }
    }
}

Lazy内部实现

kotlin 复制代码
public actual fun <T> lazy(mode: LazyThreadSafetyMode, initializer: () -> T): Lazy<T> =
    when (mode) {
        LazyThreadSafetyMode.SYNCHRONIZED -> SynchronizedLazyImpl(initializer)
        LazyThreadSafetyMode.PUBLICATION -> SafePublicationLazyImpl(initializer)
        LazyThreadSafetyMode.NONE -> UnsafeLazyImpl(initializer)
    }

Lazy接口

kotlin 复制代码
public interface Lazy<out T> {
    /**
     * Gets the lazily initialized value of the current Lazy instance.
     * Once the value was initialized it must not change during the rest of lifetime of this Lazy instance.
     */
     //当前实例化对象,一旦实例化后,该对象不会再改变
    public val value: T

    /**
     * Returns `true` if a value for this Lazy instance has been already initialized, and `false` otherwise.
     * Once this function has returned `true` it stays `true` for the rest of lifetime of this Lazy instance.
     */
     //返回true表示,已经延迟实例化过了,false表示没有被实例化
     //一旦该方法返回true,该方法会一直返回true,且不会再继续实例化.
    public fun isInitialized(): Boolean
}

SynchronizedLazyImpl内部实现

kotlin 复制代码
private class SynchronizedLazyImpl<out T>(initializer: () -> T, lock: Any? = null) : Lazy<T>, Serializable {
    private var initializer: (() -> T)? = initializer
    @Volatile private var _value: Any? = UNINITIALIZED_VALUE
    // final field is required to enable safe publication of constructed instance
    private val lock = lock ?: this

    override val value: T
        get() {
            val _v1 = _value
            //判断是否已经初始化过,如果初始化过直接返回,不再调用高级函数内部逻辑
            if (_v1 !== UNINITIALIZED_VALUE) {
                @Suppress("UNCHECKED_CAST")
                return _v1 as T
            }

            return synchronized(lock) {
                val _v2 = _value
                if (_v2 !== UNINITIALIZED_VALUE) {
                    @Suppress("UNCHECKED_CAST") (_v2 as T)
                } else {
                    //调用高级函数获取其返回值
                    val typedValue = initializer!!()
                    //将返回值赋值给_value,用于下次判断时,直接返回高级函数的返回值
                    _value = typedValue
                    initializer = null
                    typedValue
                }
            }
        }

    override fun isInitialized(): Boolean = _value !== UNINITIALIZED_VALUE

    override fun toString(): String = if (isInitialized()) value.toString() else "Lazy value not initialized yet."

    private fun writeReplace(): Any = InitializedLazyImpl(value)
}

可以看到,SynchronizedLazyImpl内部也是和java的double check lock的逻辑类似.

委托属性

属性对应的get()和set()会被委托给它的getValue()和setValue()方法.属性的委托不必实现任何的接口,但是需要提供一个getValue()函数(和setValue()-对于var属性)

在Lazy.kt文件中,声明了Lazy接口的getValue扩展函数.故在最终赋值的时候会调用该方法.

kotlin 复制代码
//返回初始化的值.
@kotlin.internal.InlineOnly
public inline operator fun <T> Lazy<T>.getValue(thisRef: Any?, property: KProperty<*>): T = value

静态内部类实现

csharp 复制代码
public class SingletonDemo4 {
    private SingletonDemo4(){
        System.out.println("Singleton has loaded");
    }

    private static class SingletonHolder{
        private static SingletonDemo4 instance=new SingletonDemo4();
    }

    public void fun(){
        System.out.println("test_fun");
    }

    public static SingletonDemo4 getInstance(){
        return SingletonHolder.instance;
    }
    
}
kotlin 复制代码
class SingletonDemoTest3 private constructor() {

    private object SingletonHolder {
        val holder = SingletonDemoTest3()
    }

    companion object {
        val instance = SingletonHolder.holder
    }

    fun test() {
        println("test1 fun")
    }

    fun test2() {
        println("test2 fun")
    }
}

fun main() {
    SingletonDemoTest3.instance.test()
    SingletonDemoTest3.instance.test2()
}

通过内部类持有外部类的引用实现调用单例的若干方法.

以上就是java和kotlin分别实现单例的若干实现方式对比

相关推荐
崔庆才丨静觅6 小时前
hCaptcha 验证码图像识别 API 对接教程
前端
passerby60616 小时前
完成前端时间处理的另一块版图
前端·github·web components
掘了7 小时前
「2025 年终总结」在所有失去的人中,我最怀念我自己
前端·后端·年终总结
崔庆才丨静觅7 小时前
实用免费的 Short URL 短链接 API 对接说明
前端
崔庆才丨静觅7 小时前
5分钟快速搭建 AI 平台并用它赚钱!
前端
崔庆才丨静觅7 小时前
比官方便宜一半以上!Midjourney API 申请及使用
前端
Moment7 小时前
富文本编辑器在 AI 时代为什么这么受欢迎
前端·javascript·后端
崔庆才丨静觅8 小时前
刷屏全网的“nano-banana”API接入指南!0.1元/张量产高清创意图,开发者必藏
前端
剪刀石头布啊8 小时前
jwt介绍
前端
爱敲代码的小鱼8 小时前
AJAX(异步交互的技术来实现从服务端中获取数据):
前端·javascript·ajax