单例模式详解:原理、实现与Android实践
1. 单例模式核心概念解析单例模式Singleton Pattern是设计模式中最简单却又最常被讨论的模式之一。这种模式的核心在于确保一个类只有一个实例并提供一个全局访问点。在实际开发中我见过太多因为滥用单例导致的线程安全问题也见证过合理使用单例带来的便利。为什么需要单例想象你正在开发一个打印机管理程序。如果允许创建多个打印管理对象可能会导致打印任务冲突、资源浪费等问题。这时单例模式就能确保整个应用程序中只有一个打印管理器实例所有打印请求都通过这个唯一实例来处理。2. 单例模式的两种基础实现方式2.1 饿汉式单例饿汉式是最直接的单例实现方式它在类加载时就创建实例。这种方式简单粗暴但可能会造成资源浪费——如果这个实例最终没有被使用。public class EagerSingleton { private static final EagerSingleton instance new EagerSingleton(); private EagerSingleton() {} public static EagerSingleton getInstance() { return instance; } }注意饿汉式的构造方法必须声明为private防止外部通过new创建实例。这是所有单例实现的基础要求。2.2 懒汉式单例与饿汉式不同懒汉式单例在第一次被调用时才创建实例。这种方式更符合按需创建的原则但需要考虑线程安全问题。public class LazySingleton { private static LazySingleton instance; private LazySingleton() {} public static synchronized LazySingleton getInstance() { if (instance null) { instance new LazySingleton(); } return instance; } }在实际项目中我通常会根据以下原则选择实现方式如果实例创建开销小且确定会被使用 → 选择饿汉式如果实例创建开销大或可能不会被使用 → 选择懒汉式3. 单例模式的进阶实现与优化3.1 双重检查锁定(DCL)直接给getInstance方法加synchronized虽然能保证线程安全但每次调用都会带来性能开销。双重检查锁定是一种优化方案public class DCLSingleton { private volatile static DCLSingleton instance; private DCLSingleton() {} public static DCLSingleton getInstance() { if (instance null) { synchronized (DCLSingleton.class) { if (instance null) { instance new DCLSingleton(); } } } return instance; } }这里有几个关键点volatile关键字防止指令重排序第一次检查避免不必要的同步第二次检查确保只有一个实例被创建3.2 静态内部类实现这是我最推荐的单例实现方式它结合了饿汉式的线程安全和懒汉式的延迟加载优点public class InnerClassSingleton { private InnerClassSingleton() {} private static class Holder { private static final InnerClassSingleton INSTANCE new InnerClassSingleton(); } public static InnerClassSingleton getInstance() { return Holder.INSTANCE; } }这种实现方式利用了类加载机制保证线程安全只有在调用getInstance()时才会加载Holder类并创建实例。4. 单例模式在Android开发中的实践在Android Studio中使用单例模式时有几个特殊注意事项Application Context的使用public class AppSingleton { private static AppSingleton instance; private Context appContext; private AppSingleton(Context context) { this.appContext context.getApplicationContext(); } public static synchronized AppSingleton getInstance(Context context) { if (instance null) { instance new AppSingleton(context); } return instance; } }重要提示永远不要持有Activity的context这会导致内存泄漏。应该使用Application Context。单例与Android组件生命周期避免在单例中直接持有View或Activity引用考虑使用WeakReference处理可能需要的上下文引用5. 单例模式的常见问题与解决方案5.1 反射攻击防护通过反射可以绕过private构造方法破坏单例。防护方法public class ReflectionProofSingleton { private static ReflectionProofSingleton instance; private ReflectionProofSingleton() { if (instance ! null) { throw new RuntimeException(Use getInstance() method to get the single instance); } } public static synchronized ReflectionProofSingleton getInstance() { if (instance null) { instance new ReflectionProofSingleton(); } return instance; } }5.2 序列化问题如果单例类实现了Serializable接口反序列化时会创建新实例。解决方法public class SerializableSingleton implements Serializable { private static final long serialVersionUID 1L; private SerializableSingleton() {} private static class Holder { private static final SerializableSingleton INSTANCE new SerializableSingleton(); } public static SerializableSingleton getInstance() { return Holder.INSTANCE; } protected Object readResolve() { return getInstance(); } }5.3 多ClassLoader环境在OSGi或某些服务器环境中不同的ClassLoader可能导致多个单例实例。解决方案是明确指定ClassLoader或使用上下文ClassLoader。6. 单例模式的最佳实践经过多年实践我总结了以下单例模式使用准则优先考虑静态内部类实现方式除非必要否则不要实现Serializable接口在Android中谨慎处理Context引用考虑使用依赖注入框架管理单例单例应该保持轻量级避免存储大量数据单元测试时要能够重置单例状态对于现代Java开发我越来越倾向于使用枚举实现单例public enum EnumSingleton { INSTANCE; public void doSomething() { // 业务方法 } }这种方式自动处理了序列化、反射等问题是最简洁安全的单例实现。但缺点是它不够灵活无法继承其他类。在Kotlin中单例实现更加简单object KotlinSingleton { fun doSomething() { // 业务方法 } }Kotlin的object关键字在底层就是使用静态内部类方式实现的单例。