一、概念
确保一个类只有一个实例,并提供全局访问点,该类负责创建自身对象,同时保证仅单个对象被创建,且可直接访问(无需实例化)。
|----|-----------------------------------------|
| 优点 | 内存中仅一个实例,减少内存开销(如频繁创建/销毁的对象)。 |
| 优点 | 避免资源多重占用(如写文件操作)。 |
| 缺点 | 无接口,不能继承,与单一职责原则冲突(一个类需同时关心内部逻辑和实例化方式)。 |
1.1 不同单例区别
|---------|-------------------------------------------------------------------------|
| 饿汉单例 | 最简洁的实现,使用Kotlin的object关键字,类加载时就初始化实例,线程安全,适合简单的单例,不依赖外部参数,启动时就需要准备好的场景。 |
| 懒汉单例 | 延迟初始化,第一次使用时才创建实例,需要使用synchronized保证线程安全,适合初始化成本高,不常使用的单例。 |
| 双重校验锁单例 | 结合了懒加载和高效的线程安全,减少了同步块的开销,只有第一次初始化时才会进入同步块,适合高性能要求的多线程环境。 |
| 静态内部类单例 | 利用类加载机制保证线程安全,延迟初始化,只有在调用getInstance()时才会加载内部类,实现简洁,性能优良。 |
| 枚举单例 | 天然防止反射和序列化攻击,是最安全的单例实现,线程安全,实现简单,适合需要高度安全性的场景,但灵活性稍差。 |
1.2 Android 使用场景
|---------------|---------------------------------------------------------|
| Application实例 | 整个应用生命周期内唯一的Application对象,用于全局状态管理(如初始化第三方库、存储全局数据)。 |
| 工具类 | 如ToastUtil、LogUtil等工具类,通过单例避免重复创建,统一管理资源(如Toast的上下文引用)。 |
| 数据库助手类 | SQLiteOpenHelper通过单例确保数据库连接唯一,避免多实例导致的锁冲突或资源泄露。 |
二、实现
1.1 饿汉单例
Kotlin
object Demo {
const val NAME = ""
fun show() {}
}
1.2 懒汉单例
Kotlin
class Demo private constructor() {
companion object {
private var instance: Demo? = null
//线程安全获取实例
@Synchronized
fun getInstance(): Demo {
if (instance == null) instance = Demo()
return instance!!
}
}
}
by lazy 版
Kotlin
class Demo private constructor() {
companion object {
val instance by lazy { Demo() }
}
}
1.3 双重校验锁单例
Kotlin
class Demo private constructor() {
companion object {
// volatile确保多线程环境下的可见性
@Volatile
private var instance: Demo? = null
fun getInstance(): Demo {
// 第一次检查,避免不必要的同步
if (instance == null) {
synchronized(Demo::class.java) {
// 第二次检查,确保只创建一个实例
if (instance == null) instance == Demo()
}
}
return instance!!
}
}
}
1.4 静态内部类单例
Kotlin
class Demo private constructor() {
companion object {
fun getInstance(): Demo = Holder.INSTANCE
}
private object Holder {
val INSTANCE = Demo()
}
}
1.5 枚举单例
Kotlin
enum class Demo {
INSTANCE;
fun show(){}
}