一、基本概念
定义
确保一个类只有一个实例,并提供一个全局访问点来访问这个实例。
核心思想
- 私有构造器:防止外部通过new创建实例
- 静态实例引用:类自身持有唯一实例
- 全局访问点:提供静态方法获取唯一实例
二、实现方式详解
1. 饿汉式 (Eager Initialization)
// Kotlin实现
object Singleton {
fun doSomething() {
println("饿汉式单例")
}
}
// 使用
Singleton.doSomething()
// Java实现
public class EagerSingleton {
// 类加载时就初始化
private static final EagerSingleton INSTANCE = new EagerSingleton();
private EagerSingleton() {
// 防止反射攻击
if (INSTANCE != null) {
throw new RuntimeException("单例模式禁止反射创建");
}
}
public static EagerSingleton getInstance() {
return INSTANCE;
}
}
特点:
- 线程安全(类加载时初始化)
- 不支持延迟加载
- 可能浪费内存(即使不用也会创建)
2. 懒汉式 (Lazy Initialization)
// 线程不安全版本
class UnsafeLazySingleton private constructor() {
companion object {
private var instance: UnsafeLazySingleton? = null
fun getInstance(): UnsafeLazySingleton {
if (instance == null) {
instance = UnsafeLazySingleton() // 多线程下可能创建多个实例
}
return instance!!
}
}
}
// 线程安全版本 - 同步方法
class SynchronizedLazySingleton private constructor() {
companion object {
private var instance: SynchronizedLazySingleton? = null
@Synchronized
fun getInstance(): SynchronizedLazySingleton {
if (instance == null) {
instance = SynchronizedLazySingleton()
}
return instance!!
}
}
}
3. 双重检查锁定 (Double-Checked Locking)
// Kotlin实现
class DoubleCheckedSingleton private constructor() {
companion object {
@Volatile // 确保可见性,防止指令重排序
private var instance: DoubleCheckedSingleton? = null
fun getInstance(): DoubleCheckedSingleton {
// 第一次检查:不加锁,提高性能
return instance ?: synchronized(this) {
// 第二次检查:加锁,确保线程安全
instance ?: DoubleCheckedSingleton().also {
instance = it
}
}
}
}
}
// Java实现
public class DoubleCheckedSingleton {
// volatile关键字防止指令重排序
private static volatile DoubleCheckedSingleton instance;
private DoubleCheckedSingleton() {}
public static DoubleCheckedSingleton getInstance() {
if (instance == null) { // 第一次检查
synchronized (DoubleCheckedSingleton.class) {
if (instance == null) { // 第二次检查
instance = new DoubleCheckedSingleton();
}
}
}
return instance;
}
}
原理分析:
- 第一次检查:避免不必要的同步
- 同步块:确保只有一个线程进入
- 第二次检查:防止多个线程通过第一次检查
- volatile:防止指令重排序,确保可见性
4. 静态内部类 (Static Inner Class)
class InnerClassSingleton private constructor() {
companion object {
fun getInstance() = Holder.INSTANCE
}
private object Holder {
val INSTANCE = InnerClassSingleton()
}
}
// Java实现
public class StaticInnerClassSingleton {
private StaticInnerClassSingleton() {}
private static class SingletonHolder {
private static final StaticInnerClassSingleton INSTANCE =
new StaticInnerClassSingleton();
}
public static StaticInnerClassSingleton getInstance() {
return SingletonHolder.INSTANCE;
}
}
优点:
- 懒加载(只有调用getInstance时才加载内部类)
- 线程安全(类加载机制保证)
- 实现简单,性能好
5. 枚举实现 (Enum Singleton) - 《Effective Java》推荐
// Kotlin
enum class EnumSingleton {
INSTANCE;
fun doSomething() {
println("枚举单例")
}
}
// 使用
EnumSingleton.INSTANCE.doSomething()
// Java
public enum EnumSingleton {
INSTANCE;
public void doSomething() {
System.out.println("枚举单例");
}
}
优势:
- 绝对防止反射攻击
- 防止反序列化创建新实例
- 线程安全
- 实现简单
三、单例模式的高级应用
1. 参数化单例
class ConfigManager private constructor(
private val configName: String
) {
private val properties = mutableMapOf<String, Any>()
companion object {
private val instances = mutableMapOf<String, ConfigManager>()
fun getInstance(configName: String): ConfigManager {
return instances[configName] ?: synchronized(this) {
instances[configName] ?: ConfigManager(configName).also {
instances[configName] = it
}
}
}
}
fun setProperty(key: String, value: Any) {
properties[key] = value
}
fun getProperty(key: String): Any? {
return properties[key]
}
}
// 使用不同的配置实例
val userConfig = ConfigManager.getInstance("user")
val systemConfig = ConfigManager.getInstance("system")
2. 单例工厂
abstract class SingletonFactory<T> {
private val instances = mutableMapOf<String, T>()
protected abstract fun create(key: String): T
fun getInstance(key: String): T {
return instances[key] ?: synchronized(this) {
instances[key] ?: create(key).also {
instances[key] = it
}
}
}
}
// 具体实现
class DatabaseFactory : SingletonFactory<Database>() {
override fun create(key: String): Database {
return when (key) {
"user" -> UserDatabase()
"product" -> ProductDatabase()
else -> DefaultDatabase()
}
}
}
四、单例模式的问题与解决方案
1. 内存泄漏问题
// 错误示例:单例持有Activity引用
class LeakySingleton private constructor(private val activity: Activity) {
companion object {
private var instance: LeakySingleton? = null
fun init(activity: Activity) {
if (instance == null) {
instance = LeakySingleton(activity) // 持有Activity引用!
}
}
fun getInstance(): LeakySingleton? = instance
}
}
// 正确做法:使用Application Context
class SafeSingleton private constructor(context: Context) {
private val appContext = context.applicationContext
companion object {
@Volatile
private var instance: SafeSingleton? = null
fun getInstance(context: Context): SafeSingleton {
return instance ?: synchronized(this) {
instance ?: SafeSingleton(context.applicationContext).also {
instance = it
}
}
}
}
}
2. 测试困难 - 使用依赖注入解决
// 传统单例 - 难以测试
class AnalyticsManager private constructor() {
companion object {
private var instance: AnalyticsManager? = null
fun getInstance(): AnalyticsManager {
return instance ?: synchronized(this) {
instance ?: AnalyticsManager().also {
instance = it
}
}
}
}
fun trackEvent(event: String) {
// 发送到服务器 - 测试时难以模拟
}
}
// 使用接口和依赖注入
interface Analytics {
fun trackEvent(event: String)
}
class AnalyticsImpl : Analytics {
override fun trackEvent(event: String) {
// 真实实现
}
}
class MockAnalytics : Analytics {
override fun trackEvent(event: String) {
// 测试实现
}
}
// 使用Dagger/Hilt依赖注入
@Module
@InstallIn(SingletonComponent::class)
object AnalyticsModule {
@Provides
@Singleton // Dagger会处理单例
fun provideAnalytics(): Analytics = AnalyticsImpl()
}
// ViewModel中使用
class MyViewModel @Inject constructor(
private val analytics: Analytics // 由Dagger注入单例
) : ViewModel() {
fun onButtonClick() {
analytics.trackEvent("button_click")
}
}
五、Kotlin特有的单例特性
1. object声明
object NetworkManager {
private const val TIMEOUT = 5000L
fun makeRequest(url: String) {
// 网络请求
}
// object可以有init块
init {
println("NetworkManager初始化")
}
}
// 反编译为Java看本质
/*
public final class NetworkManager {
public static final NetworkManager INSTANCE;
static {
NetworkManager var0 = new NetworkManager();
INSTANCE = var0;
System.out.println("NetworkManager初始化");
}
private NetworkManager() {}
}
*/
2. by lazy实现懒加载
class LazySingleton private constructor() {
companion object {
val instance: LazySingleton by lazy(LazyThreadSafetyMode.SYNCHRONIZED) {
println("初始化LazySingleton")
LazySingleton()
}
}
}
// 不同的线程安全模式
val unsafeInstance by lazy(LazyThreadSafetyMode.NONE) { /* 线程不安全 */ }
val synchronizedInstance by lazy(LazyThreadSafetyMode.SYNCHRONIZED) { /* 线程安全 */ }
val publicationInstance by lazy(LazyThreadSafetyMode.PUBLICATION) { /* 允许重复初始化 */ }
六、最佳实践指南
何时使用单例模式?
✅ 适用场景:
- 全局配置管理(如AppConfig)
- 日志记录器(如Logger)
- 数据库访问对象
- 网络请求客户端
- 缓存管理器
- 工具类(如DateFormatter)
❌ 避免使用场景:
- 需要大量测试的类
- 可能有多态需求的类
- 需要参数化配置的类
- 状态频繁变化的类
Android开发建议
- 优先使用依赖注入(Dagger/Hilt)管理单例
- 避免持有Context引用,必须使用时用Application Context
- 注意生命周期,及时清理资源
- 使用Kotlin object简化单例实现
代码示例:完整的Android单例
// 使用Kotlin object + 依赖注入思想
object ImageLoader {
private const val MEMORY_CACHE_SIZE = 10 * 1024 * 1024 // 10MB
private val memoryCache: LruCache<String, Bitmap>
private val executor: ExecutorService
init {
memoryCache = object : LruCache<String, Bitmap>(MEMORY_CACHE_SIZE) {
override fun sizeOf(key: String, value: Bitmap): Int {
return value.byteCount
}
}
executor = Executors.newFixedThreadPool(4)
}
fun loadImage(url: String, imageView: ImageView) {
// 检查内存缓存
val cachedBitmap = memoryCache.get(url)
if (cachedBitmap != null) {
imageView.setImageBitmap(cachedBitmap)
return
}
// 异步加载
executor.submit {
val bitmap = downloadImage(url)
bitmap?.let {
memoryCache.put(url, it)
// 切回主线程更新UI
Handler(Looper.getMainLooper()).post {
imageView.setImageBitmap(it)
}
}
}
}
fun clearCache() {
memoryCache.evictAll()
}
}
// 使用
ImageLoader.loadImage("https://example.com/image.jpg", imageView)
七、总结
单例模式是Android开发中最常用的设计模式之一,但也是最容易被滥用的模式。正确使用单例模式需要注意:
- 线程安全:根据需求选择合适的线程安全方案
- 内存管理:避免内存泄漏,特别是Context引用
- 测试友好:考虑使用依赖注入提高可测试性
- Kotlin特性:充分利用object、by lazy等语言特性
- 架构考虑:在MVVM等架构中合理使用单例