设计模式 - 单例模式 Singleton Pattern

一、概念

确保一个类只有一个实例,并提供全局访问点,该类负责创建自身对象,同时保证仅单个对象被创建,且可直接访问(无需实例化)。

优点内存中仅一个实例,减少内存开销(如频繁创建/销毁的对象)。
避免资源多重占用(如写文件操作)。
缺点无接口,不能继承,与单一职责原则冲突(一个类需同时关心内部逻辑和实例化方式)。

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 }
}

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值