【Java设计模式 | 创建者模式】单例模式
前言创建者模式主要关注是“怎么创建对象”它的主要特点是将对象的创建和使用分离。这样可以降低系统的耦合度使用者不需要关注对象的创建细节。创建模式分为单例模式Singleton【Java设计模式 | 创建者模式】单例模式-CSDN博客工厂方法模式Factory Method【Java设计模式 | 创建者模式】工厂方法模式-CSDN博客抽象工厂模式Abstract Factory【Java设计模式 | 创建者模式】 抽象工厂模式-CSDN博客建造者模式Builder原型模式Prototype单例设计模式单例模式Singleton是Java中最简单的设计模式之一。它提供了一种创建对象的最佳方式。这种模式涉及到一个单一的类该类负责创建自己的对象同时确保只有一个对象被创建。这个类提供了一种访问其唯一的对象的方式可以直接访问不需要实例化该类的对象。单例模式结构单例模式主要有以下角色单例类只能创建一个实例的类。访问类使用单例类。单例模式的实现单例模式主要分为饿汉式类加载就会导致该单实例对象被创建避免了线程同步问题。懒汉式类加载不会导致该单实例对象被创建而是首次使用该对象时才会被创建。一、饿汉式实现优点实现简单线程安全缺点可能造成资源浪费如果实例未被使用1.静态变量方式私有构造函数确保外部无法通过new创建实例。通过静态方法getInstance()提供全局唯一访问点。public class Singleton { // 在类加载时创建实例 private static final Singleton instance new Singleton(); // 私有构造函数防止外部实例化 private Singleton() {} // 提供全局访问点 public static Singleton getInstance() { return instance; } }2.静态代码块这种实现方式与直接静态变量方式的本质相同。静态代码块方式适合需要更复杂初始化逻辑的场景。私有构造函数防止外部实例化全局唯一访问点通过getInstance()方法提供public class Singleton { // 静态实例变量 private static Singleton instance; // 静态代码块初始化实例 static { instance new Singleton(); } // 私有构造函数防止外部实例化 private Singleton() {} // 提供全局访问点 public static Singleton getInstance() { return instance; } }二、懒汉式实现懒汉式单例模式的特点是延迟初始化即在第一次调用时才创建实例。需要判断instance是否为null3.线程不安全如果有多个线程可能存在创建多个实例的情况导致线程不安全。public class Singleton { private static Singleton instance; private Singleton() {} public static Singleton getInstance() { if (instance null) { instance new Singleton(); } return instance; } }4.线程安全可以通过添加synchronized关键字实现线程安全。public class Singleton { private static Singleton instance; private Singleton() {} public static synchronized Singleton getInstance() { if (instance null) { instance new Singleton(); } return instance; } }5.双重检查锁直接同步整个方法会影响性能可以使用双重检查锁定来减少同步范围。注意volatile关键字的使用它防止指令重排序导致的初始化问题例如多线程情况下可能出现空指针问题。设计到并发编程就不过多讲解。public class Singleton { private static volatile Singleton instance; private Singleton() {} public static Singleton getInstance() { if (instance null) { synchronized (Singleton.class) { if (instance null) { instance new Singleton(); } } } return instance; } }6.静态内部类推荐由于JVM在加载外部类的过程中不会加载静态内部类只有内部类的属性/方法被调用时才会被加载并初始化其静态属性。静态属性由于被static修饰保证之后被实例化一次并且严格保证实例化顺序。线程安全由JVM保证。相对于多重检查锁实现单例模式静态内部类的方式在保证了线程安全的前提下实现更加的简单。public class Singleton { private Singleton() { // 私有构造函数防止外部实例化 } private static class SingletonHolder { private static final Singleton INSTANCE new Singleton(); } public static Singleton getInstance() { return SingletonHolder.INSTANCE; } }7.枚举方式枚举类是单例模式实现极力推荐的方式。属于饿汉式方式在不考虑内存空间的情况下首先考虑枚举方式。优点这是实现单例模式最简洁安全的方式天生支持序列化机制防止反射攻击线程安全由JVM保证public enum Singleton { INSTANCE; public void doSomething() { // 业务方法 } }单例模式的破坏使上面定义的单例类Singleton可以创建多个对象枚举方式除外。有两种方式序列化和反射。序列化和反序列化通过序列化和反序列化可以破坏单例模式关键在于反序列化时会创建新的对象实例。import java.io.*; // 可被序列化破坏的单例类 class Singleton implements Serializable { private static final long serialVersionUID 1L; private static Singleton instance new Singleton(); private Singleton() {} public static Singleton getInstance() { return instance; } } public class BreakSingleton { public static void main(String[] args) throws Exception { Singleton instance1 Singleton.getInstance(); // 序列化到文件 ObjectOutput out new ObjectOutputStream(new FileOutputStream(singleton.ser)); out.writeObject(instance1); out.close(); // 反序列化 ObjectInput in new ObjectInputStream(new FileInputStream(singleton.ser)); Singleton instance2 (Singleton) in.readObject(); in.close(); System.out.println(instance1 hashCode: instance1.hashCode()); System.out.println(instance2 hashCode: instance2.hashCode()); System.out.println(Is same instance? (instance1 instance2)); } }防止序列化和反序列化破坏的方法实现readResolve()方法是防止序列化破坏单例的关键。当JVM反序列化对象时会检查类是否定义了此方法。如果存在JVM会自动调用该方法获取对象而不是新建实例。import java.io.*; // 防止序列化破坏的单例类 class SafeSingleton implements Serializable { private static final long serialVersionUID 1L; private static SafeSingleton instance new SafeSingleton(); private SafeSingleton() {} public static SafeSingleton getInstance() { return instance; } // 关键方法反序列化时返回已有实例 protected Object readResolve() { return instance; } } public class SafeSingletonDemo { public static void main(String[] args) throws Exception { SafeSingleton instance1 SafeSingleton.getInstance(); // 序列化 ObjectOutput out new ObjectOutputStream(new FileOutputStream(safe.ser)); out.writeObject(instance1); out.close(); // 反序列化 ObjectInput in new ObjectInputStream(new FileInputStream(safe.ser)); SafeSingleton instance2 (SafeSingleton) in.readObject(); in.close(); System.out.println(instance1 hashCode: instance1.hashCode()); System.out.println(instance2 hashCode: instance2.hashCode()); System.out.println(Is same instance? (instance1 instance2)); } }反射破坏单例模式通常通过私有构造函数和静态方法确保只有一个实例。反射可以绕过这些限制直接调用私有构造函数创建新实例。import java.lang.reflect.Constructor; public class BreakSingleton { public static void main(String[] args) throws Exception { Singleton instance1 Singleton.getInstance(); // 获取私有构造函数 ConstructorSingleton constructor Singleton.class.getDeclaredConstructor(); constructor.setAccessible(true); // 突破私有访问限制 // 创建第二个实例 Singleton instance2 constructor.newInstance(); System.out.println(Instance 1 hash: instance1.hashCode()); System.out.println(Instance 2 hash: instance2.hashCode()); System.out.println(Are instances equal? (instance1 instance2)); } }防止反射破坏的方法方法一在单例类的私有构造函数中添加实例存在性检查。简单有效但懒汉式多线程下仍有有漏洞。public class Singleton { private static Singleton instance new Singleton(); private Singleton() { // 防止反射破坏单例的可选检查 if (instance ! null) { throw new RuntimeException(Attempt to create duplicate singleton instance); } } public static Singleton getInstance() { return instance; } }方法二通过双重检查锁定DCL实现线程安全的懒汉式单例并增加反射防护机制volatile关键字确保多线程环境下instance变量的可见性防止指令重排序导致的未初始化对象被引用。私有构造函数防护在构造函数中检查实例是否已存在若通过反射调用构造函数会直接抛出异常。双重检查锁定外层判断避免每次获取实例都加锁内层判断确保多线程环境下只创建一个实例。public class Singleton { private static volatile Singleton instance; private Singleton() { if (instance ! null) { throw new IllegalStateException(Singleton instance already exists); } } public static Singleton getInstance() { if (instance null) { synchronized (Singleton.class) { if (instance null) { instance new Singleton(); } } } return instance; } }