在 Kotlin 中创建单例
设计模式是一组基于手头需求的常见问题的解决方案,这些解决方案可以在试图解决相同问题的其他系统中重复使用。 有不同的设计模式,包括创建型、行为型和结构型设计模式。
创建型设计模式处理如何创建对象,在本教程中将学习如何使用单例模式,这是创建型设计模式的一个示例。
单例模式是一种设计模式,有助于创建一个对象的单个副本并提供一个访问点。
在开发应用程序时,有些情况下我们需要一个对象的单个副本,例如线程池、日志记录、设备驱动程序、注册表设置、缓存等的对象。
具有这些对象的多个副本的程序可能会导致不正确的行为、资源过度使用和不一致的结果。 在本教程中,我们将学习在 Kotlin 中创建单例的不同方法。
使用 Kotlin API 创建单例
Kotlin 提供了一个单例,我们可以开箱即用,无需为其编写任何对象创建代码。 为此,我们必须使用 object 关键字定义我们的单例名称,并且在这个单例中,我们可以添加任何成员变量和函数。
打开 IntelliJ 并选择文件 > 新建 > 项目。 在打开的窗口中,输入项目名称 kotlin-singleton,在 Language 部分选择 Kotlin,在 Build system 部分选择 Intellij。
最后,按创建按钮生成项目。
在 src/main/kotlin 文件夹下创建一个名为 Main.kt 的文件,并将以下代码复制并粘贴到该文件中。
object Singleton{
fun showMessage(): Singleton {
return Singleton
}
}
fun main(){
println(Singleton.showMessage())
println(Singleton.showMessage())
}
如上所述,这是 Kotlin 中创建单例的默认方式,并且非常容易实现。 请注意,创建的单例对象是线程安全的,这意味着即使是多线程程序也无法创建可能导致数据不一致的新对象。
在这段代码中,我们创建了一个名为 Singleton 的单例和一个名为 showMessage() 的成员函数。 该函数返回对创建的单例的引用。
运行此代码并注意对成员函数的两次调用返回对同一对象的引用,如下所示。
Singleton@5305068a
Singleton@5305068a
使用 Companion 对象创建单例
将前面的例子注释掉,将下面的代码复制粘贴到Main.kt文件注释后。
class Singleton private constructor(){
companion object{
var singleton = Singleton();
fun getInstance(): Singleton{
if (singleton == null){
singleton = Singleton();
}
return singleton;
}
}
}
fun main(){
println(Singleton.getInstance());
println(Singleton.getInstance());
}
前一个例子和这个例子的唯一区别是伴随对象是在一个类中使用的。 Kotlin 中的所有静态变量和函数都放在伴生对象中。
在这段代码中,我们创建了一个名为 getInstance() 的方法,它确保只创建对象的一个副本。 为了确保遵守这一点,我们将 constructor()
设为私有,并确保在创建对象之前检查对象是否存在。
请注意
,这种方法效果很好,但它不是线程安全的。 如果一个应用程序是多线程的,它可以同时访问getInstance()
方法,从而导致多个对象,从而导致前面提到的问题。
下一节将展示如何使单例线程安全。
运行此代码并确保输出返回对如下所示的同一对象的引用。
Singleton@1f32e575
Singleton@1f32e575
使用Companion对象创建线程安全的单例
将之前的代码注释掉,将下面的代码复制粘贴到Main.kt文件注释后。
class Singleton private constructor(){
companion object{
private var singleton = Singleton();
@Synchronized
fun getInstance(): Singleton{
if (singleton == null){
singleton = Singleton();
}
return singleton
}
}
}
请注意,这段代码与前面的代码类似; 唯一的区别是我们在 getInstance() 方法上添加了 @Synchronized 注释。
这种方法在 Java 中称为同步方法。 线程安全是通过锁来实现的。
对象通常有锁,当一个线程到达同步方法时,它独占地获得这个锁,直到它创建完一个对象,这意味着没有其他线程可以访问这个方法。
当另一个线程访问锁时,我们已经有一个对象,线程使用现有线程而不创建新线程。 还有其他方法可以实现同步,比如使用synchronized块,它可以帮助我们同步一段代码而不是整个方法。
请注意
,同步解决了我们的问题,但在我们的应用程序中引入了性能问题。 同步会导致我们应用程序的性能降低 100 倍,如果我们的应用程序对性能至关重要,我们应该重新考虑使用它。
我们可以使用从惰性初始化转变为急切初始化、双重检查锁定的方法,或者在性能不会影响应用程序的情况下保留它。
使用 Lazy Initializer 创建单例
将之前的代码注释掉,将下面的代码复制粘贴到Main.kt文件注释后。
class Singleton{
companion object{
val singleton: Singleton by lazy {
Singleton();
}
}
fun showMessage(): Singleton {
return Singleton.singleton
}
}
fun main(){
println(Singleton.singleton.showMessage())
println(Singleton.singleton.showMessage())
}
在这段代码中,我们使用函数 lazy() 创建了一个单例,它使用我们传递给它的参数创建了一个线程安全的 Lazy 实例。 它使用 LazyThreadSafetyMode.SYNCHRONIZED 来确保只有一个锁用于初始化惰性实例。
运行此代码并确保输出返回对如下所示的同一对象的引用。
Singleton@3e3abc88
Singleton@3e3abc88
总结
在本篇文章中,我们学习了在 Kotlin 中创建单例对象的不同方法。 涵盖的方法包括使用 object 关键字、伴生对象和惰性初始化函数。
使用 synchronized 方法,我们还学习了如何在使用伴随对象时确保我们的单例对象是线程安全的。
相关文章
Kotlin 中 Java String[] 的等价物
发布时间:2023/05/13 浏览次数:59 分类:Java
-
本文介绍了 Kotlin 中 Java String[] 的等价物。 我们将看到所有可能的方法来为 Kotlin 实现与 Java 中的 String[] 相同的结果。
将 Java 文件代码转换为 Kotlin
发布时间:2023/05/13 浏览次数:142 分类:Java
-
Kotlin 现在是一种官方的 Android 语言。 因此,您可能希望将 Java 文件更改为 Kotlin。 本文教您如何将 Java 转换为 Kotlin。
在 Kotlin 中使用 forEach
发布时间:2023/05/13 浏览次数:122 分类:Java
-
本文介绍 Kotlin 中 forEach 关键字的概念和使用。 我们将看到一些使用 Kotlin forEach 循环的示例来理解它。
在 Kotlin 中使用 reified 关键字
发布时间:2023/05/13 浏览次数:197 分类:Java
-
reified 关键字是在 Kotlin 中使用泛型时最常使用的编程概念。在本教程中,我们将学习如何使用两种方法解决此问题,包括将类型的类作为泛型函数的参数传递,以及将 reified 关键字与内联函数
Kotlin 中 ! 和 ? 运算符之间的区别
发布时间:2023/05/13 浏览次数:180 分类:Java
-
在 Kotlin 中,断言运算符 !! 和安全调用符 ? 使用 Null 安全。本文介绍空安全的概念。 我们也将通过如何! 和 ? 在 Kotlin 中有助于空安全。
Kotlin 中 is 运算符 与 as 运算符之间的区别
发布时间:2023/05/13 浏览次数:148 分类:Java
-
这篇文章讨论了 Kotlin 中 is 和 as 运算符之间的区别。 我们还将介绍如何以及何时使用每一个。
Android Studio 中的 Kotlin 打印到控制台
发布时间:2023/05/13 浏览次数:52 分类:Java
-
Kotlin 的 logcat 窗口可以实时显示输出,让开发人员能够高效地处理他们的代码。 今天,我们将完成将消息打印到 Kotlin 控制台的步骤。
在 Kotlin 中获取随机数
发布时间:2023/05/13 浏览次数:68 分类:Java
-
随机数是使用一种算法从一组数字中生成的,该算法可确保每个数字的生成概率相等。在Kotlin中使用IntRange的random()扩展函数
在 Kotlin 中使用内联函数
发布时间:2023/05/13 浏览次数:199 分类:Java
-
Kotlin 允许使用内联函数有效地处理更高级的函数。 在本文中,我们将介绍我们需要了解的有关 Kotlin 中的内联函数的所有信息。