注册

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

单例模式


单例模式是一个很常见的设计模式,尤其是在 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 也会提醒你这样会造成内存泄漏:


image-20230216202522160.png


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


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

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

var view: View? = null
}

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


image-20230216202859206.png


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


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

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


image-20230216203102547.png


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


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


写在最后


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


作者:自动化BUG制造器
链接:https://juejin.cn/post/7200708877389070395
来源:稀土掘金
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

0 个评论

要回复文章请先登录注册