likes
comments
collection
share

【设计模式】Kotlin 与 Java 中的单例

作者站长头像
站长
· 阅读数 15

单例模式

单例模式是一个很常见的设计模式,尤其是在 Java 中应用非常广泛。单例模式的定义是保证一个类仅有一个实例,并提供一个访问它的全局访问点。

Java 中的单例模式

Java 中存在多种单例模式的实现方案,最经典的包括:

  • 懒汉式

  • 饿汉式

  • 双重校验锁

饿汉式 / 静态单例

Java 中懒汉式单例如其名一样,“饿” 体现在不管单例对象是否存在,都直接进行初始化:

public final class SingleManager {
    @NotNull
    public static final SingleManager INSTANCE = new SingleManager();

    private SingleManager() {}

    public static SingleManager getInstance() {
        return INSTANCE;
    }
}

实际上,这也是静态单例。

懒汉式 / 延迟初始化

Java 中的懒汉式核心特点在于“懒” ,它不像饿汉式,无论 INSTANCE 是否已经存在值,都进行初始化;而是在调用 get 方法时,检查引用对象是否为空,如果为空再去初始化:

public final class SingleManager {
    public static SingleManager INSTANCE;

    private SingleManager() {}

    public static SingleManager getInstance() {
        if (INSTANCE == null) {
            INSTANCE = new SingleManager();
        } 
        return INSTANCE;
    }
}

双重校验锁

public final class SingleManager {
    // volatile 防止指令重排,确保原子操作的顺序性
    public volatile static SingleManager INSTANCE;

    private SingleManager() {}

    public static SingleManager getInstance() {
        // 第一次判空,减少进入同步锁的次数,提高效率
        if (INSTANCE == null) {
            // 确保同步
            synchronized (SingleManager.class) {
                // 确保加锁后,引用仍是空的
                if (INSTANCE == null) {
                    INSTANCE = new SingleManager();
                }
            }
        }
        return INSTANCE;
    }
}

Kotlin 中的单例模式

object 关键字

Kotlin 提供了比 Java 更方便的语法糖 object 关键字,能够更方便地实现单例模式:

object SingleManager {
    fun main() {}
}

使用:

// used in kotlin
SingleManager.main()
// used in java
SingleManager.Companion.main();

如果要在 Java 中的使用方式与 Kotlin 使用方式一致,可以在方法上添加 @JvmStatic 注解:

object SingleManager {
    @JvmStatic
    fun main() {}
}
// used in java
SingleManager.main();

object 关键字实现的单例,编译为 Java 字节码的实现是:

public final class SingleManager {
   @NotNull
   public static final SingleManager INSTANCE;

   public final void main() {
   }

   private SingleManager() {
   }

   static {
      SingleManager var0 = new SingleManager();
      INSTANCE = var0;
   }
}

这是一种标准的 Java 静态单例实现。

Kotlin 懒汉式

在一些特殊的情况,例如你的单例对象要保存一些不适合放在静态类中的引用,那么使用 object 就不是合适的方案了,例如,Android 中的上下文 Context 、View 都不适合在静态类中进行引用,IDE 也会提醒你这样会造成内存泄漏:

【设计模式】Kotlin 与 Java 中的单例

一种好的解决方案是在 Kotlin 中使用懒汉式的写法:

class SingleManager {
    companion object {
        private var instance: SingleManager? = null

        fun getInstance(): SingleManager {
            if (instance == null) {
                instance = SingleManager()
            }
            return instance!!
        }
    }

    var view: View? = null
}

但是这样仍然会提醒你不要引用:

【设计模式】Kotlin 与 Java 中的单例

但如果引用的对象是你自定义的 View :

class BaseView @JvmOverloads constructor(
    context: Context, attrs: AttributeSet? = null
) : FrameLayout(context, attrs)

在 kotlin 中懒汉式是不会提示你的:

【设计模式】Kotlin 与 Java 中的单例

可以看出,使用 object 关键字,仍然会飘黄,提醒你可能存在内存泄漏。

本质上来说,没有提示实际上也是存在内存泄漏的隐患的。 虽然可以骗过 IDE 但不应该欺骗自己。

写在最后

Kotlin 作为一门更新的 JVM 语言,它提供了很多语法糖突破了 Java 的一些固定写法,有些设计模式已经不再适合新的语言(例如 Builder 模式在 Kotlin 中很少会出现了)。虽然新语言简化了代码的复杂度、简化了写法,但不能简化知识点,例如,使用 Kotlin 需要一个线程安全的单例,仍然可以使用双重校验锁的写法。本质上还是要搞清楚底层逻辑。