一、概念
确保一个类只有一个实例,并提供全局访问点,该类负责创建自身对象,同时保证仅单个对象被创建,且可直接访问(无需实例化)。
| 优点 | 内存中仅一个实例,减少内存开销(如频繁创建/销毁的对象)。 |
| 避免资源多重占用(如写文件操作)。 |
| 缺点 | 无接口,不能继承,与单一职责原则冲突(一个类需同时关心内部逻辑和实例化方式)。 |
1.1 Java不同单例区别
| Java类加载时机 | 创建类的实例对象时:当通过new关键字实例化一个类时,JVM会加载该类。 |
| 访问类的静态变量或方法时:如果类中包含静态变量或静态方法,访问它们会触发类加载。 |
| 调用类的静态代码块时:静态代码块在类加载时会被执行。 |
| 使用Class.forName()方法时:通过反射机制加载类时会触发类加载。 |
| 调用主类的main方法时:程序启动时,JVM会加载包含main方法的类。 |
懒加载(延迟初始化):类加载时属性会初始化,Java属性不赋值默认为null,提供获取实例的方法,待到外部调用时,该属性为null就创建实例赋值该属性,不为null就返回该属性。在多线程环境下会有并发问题导致创建多个实例,需要通过上锁,不同上锁的方式效率越高写起来越麻烦。
| 饿汉单例 | 最简洁的实现,使用Kotlin的object关键字,类加载时就初始化实例,线程安全,适合简单的单例,不依赖外部参数,启动时就需要准备好的场景。 |
| 懒汉单例 | 延迟初始化,第一次使用时才创建实例,需要使用synchronized保证线程安全,适合初始化成本高,不常使用的单例。 |
| 双重校验锁单例 | 结合了懒加载和高效的线程安全,减少了同步块的开销,只有第一次初始化时才会进入同步块,适合高性能要求的多线程环境。 |
| 静态内部类单例 | 利用类加载机制保证线程安全,延迟初始化,只有在调用getInstance()时才会加载内部类,实现简洁,性能优良。 |
| 枚举单例 | 天然防止反射和序列化攻击,是最安全的单例实现,线程安全,实现简单,适合需要高度安全性的场景,但灵活性稍差。 |
1.2 Android 使用场景
| Application实例 | 整个应用生命周期内唯一的Application对象,用于全局状态管理(如初始化第三方库、存储全局数据)。 |
| 工具类 | 如ToastUtil、LogUtil等工具类,通过单例避免重复创建,统一管理资源(如Toast的上下文引用)。 |
| 数据库助手类 | SQLiteOpenHelper通过单例确保数据库连接唯一,避免多实例导致的锁冲突或资源泄露。 |
二、Kotlin实现
1.1 无参单例
| object | 饿汉,线程安全(类加载时被初始化由JVM保证线程安全)。 |
| 伴生对象 + by lazy | 懒汉(属性通过 by lazy 延迟到第一次获取时初始化),线程安全(by lazy默认同步)。 |
| 伴生对象 + 同步 | 懒汉(属性初始化为null,第一次获取时赋值),线程安全(同步方式性能低)。 |
| 伴生对象 + 嵌套object | 懒汉(内部object类是嵌套类,也就是静态内部类,不调用获取方法不会加载嵌套类),线程安全(object线程安全)。 |
| 枚举单例 | 饿汉,线程安全(类加载时被初始化由JVM保证线程安全)。枚举不能继承父类只能实现接口。 |
1.1.1 object
object Demo {
var age = 18
const val NAME = ""
fun show() {}
}
1.1.2 伴生对象 + by lazy
class Demo private constructor() {
companion object {
val instance by lazy { Demo() }
}
}
1.1.3 伴生对象 + 同步
class Demo private constructor() {
companion object {
private var instance: Demo? = null
@Synchronized
fun getInstance(): Demo {
if (instance == null) instance = Demo()
return instance!!
}
}
}
//上面 getInstance() 可简写
fun getInstance() = instance ?: synchronized(this) {
Demo().also { instance = it }
}
1.1.4 伴生对象 + 内部object
class Demo private constructor() {
companion object {
fun getInstance(): Demo = Holder.INSTANCE
}
private object Holder {
val INSTANCE = Demo()
}
}
1.1.5 枚举单例
enum class Demo {
INSTANCE;
fun show(){}
}
1.2 需要传参
| 伴生对象 + 同步 | 效率低,代码简单。 |
| 伴生对象 + 双重校验锁 | 更高效,代码复杂。 |
1.2.1 伴生对象 + 同步
class Demo private constructor(
val name: String
) {
companion object {
private var instance: Demo? = null
@Synchronized
fun getInstance(name: String): Demo {
if (instance == null) instance = Demo(name)
return instance!!
}
}
}
//上面 getInstance() 可简写
fun getInstance2(name: String) = instance ?: synchronized(this) {
Demo(name).also { instance = it }
}
1.2.2 伴生对象 + 双重校验锁
class Demo private constructor(
val name: String
) {
companion object {
// volatile确保多线程环境下的可见性
@Volatile
private var instance: Demo? = null
fun getInstance(name: String): Demo {
//第一次检查,避免不必要的同步
if (instance == null) {
synchronized(this) {
// 第二次检查,确保只创建一个实例
if (instance == null) instance == Demo(name)
}
}
return instance!!
}
}
}
//上面 getInstance() 可简写
fun getInstance(name: String) = instance ?: synchronized(this) {
instance ?: Demo(name).also { instance = it }
}