创建型设计模式:关注对象的创建过程 单例模式、工厂方法模式、抽象工厂模式、建造者模式、原型模式

一、单例模式

表示全局唯一 处理资源访问冲突

1、实现方式

1.1、饿汉式

在类加载的时候初始化静态变量

public class EagerSingleton {  
    private static Singleton instance = new Singleton();  
    private Singleton (){}  
    public static Singleton getInstance() {  
        return instance;  
    }  
}

1.2、懒汉式

在获取对象的时候进行加载,面对大量并发请求的时候加锁会降低获取单例对象的并发度

    private static Singleton instance;  
    private Singleton (){}  
    public synchronized static Singleton getInstance() {  
        if (instance == null) {  
            instance = new Singleton();  
        }  
        return instance;  
    }  
}

1.3、双重检查锁

既支持延迟加载、又支持高并发的单例实现方式;

Image

volatile:不稳定、无定形
在Java中修饰变量可确保一个线程修改该变量的值后,修改后的新值是对其他线程是立即可见的
工作内存是CPU的缓存区,多线程操作的时候,线程访问自己的工作内存
加上关键词volatile保证可见性

public class DclSingleton {  
    private volatile static Singleton singleton;  
    private Singleton (){}  

    public static Singleton getInstance() {  
        if (singleton == null) {  
            synchronized (Singleton.class) {  
                if (singleton == null) {  
                    singleton = new Singleton();  
                }  
            }  
        }  
        return singleton;  
    }  
}

1.4、静态内部类

Java 的静态内部类;类 Singleton被加载的时候
SingletonHolder 这个静态内部类,只会在第一次被使用时加载并初始化;
JVM 在执行类的 () 方法时,会加锁、同步,确保多线程下也只执行一次。

public class InnerSingleton {

    /** 私有化构造器 */
    private Singleton() {
    }

    /** 对外提供公共的访问方法 */
    public static Singleton getInstance() {
        return SingletonHolder.INSTANCE;
    }

    /** 写一个静态内部类,里面实例化外部类 */
    private static class SingletonHolder {
        private static final Singleton INSTANCE = new Singleton();
    }

}

1.5、枚举

枚举类中,每一个枚举项本身就是一个单例;枚举类在加载时就初始化实例,且只执行一次,线程安全由 JVM 保证;

public enum SingletonEnum {

    // 唯一的枚举实例
    INSTANCE;

    // 可以添加你的业务方法
    public void doSomething() {
        System.out.println("单例方法执行");
    }
}

2、漏洞

2.1、反射注入构造函数

上述的单例模式之中,私有化构造器并不能阻止其他人通过反射的方式进行注入(枚举除外);
需要在构造函数之中多进行一次处理

    private SingletonExample() {
        // 防止通过反射创建多个实例
        if (instance != null) {
            throw new RuntimeException("禁止通过反射创建第二个实例!");
        }
    }
    /**
     * 通过反射创建指定类的实例
     * 
     * @param clazz 要实例化的类
     * @param <T> 类型参数
     * @return 创建的新实例
     * @throws RuntimeException 如果实例化失败
     */
    public static <T> T createInstance(Class<T> clazz) {
        try {
            Constructor<T> constructor = clazz.getDeclaredConstructor();
            constructor.setAccessible(true);
            return constructor.newInstance();
        } catch (NoSuchMethodException e) {
            throw new RuntimeException("找不到无参构造函数: " + clazz.getName(), e);
        } catch (InstantiationException e) {
            throw new RuntimeException("无法实例化: " + clazz.getName(), e);
        } catch (IllegalAccessException e) {
            throw new RuntimeException("访问被拒绝: " + clazz.getName(), e);
        } catch (InvocationTargetException e) {
            throw new RuntimeException("构造函数调用失败: " + clazz.getName(), e);
        }
    }

2.2、序列化与反序列化安全

反序列化创建新对象,不会调用任何构造方法,它直接从流里恢复字段,绕开了 Java 的对象创建正常流程。
你的私有构造器、你的 instance 判断,对反序列化完全无效!
必须添加readResolve方法:反序列化完新对象后,丢掉这个新对象,直接返回单例对象。

private Object readResolve() {
    return getInstance();
}


 // 获取原始单例对象
        SafeSerializableSingleton original = SafeSerializableSingleton.getInstance();
        System.out.println("原始实例 hashCode: " + original.hashCode());
        System.out.println("原始实例地址:" + original);
        
        // 序列化到文件
        String fileName = "safe_singleton.ser";
        try (
            FileOutputStream fileOut = new FileOutputStream(fileName);
            ObjectOutputStream out = new ObjectOutputStream(fileOut)
        ) {
            out.writeObject(original);
            System.out.println("\n✓ 对象已序列化到文件:" + fileName);
        } catch (IOException e) {
            System.err.println("序列化失败:" + e.getMessage());
            return;
        }
        
        // 从文件反序列化
        SafeSerializableSingleton deserialized = null;
        try (
            FileInputStream fileIn = new FileInputStream(fileName);
            ObjectInputStream in = new ObjectInputStream(fileIn)
        ) {
            deserialized = (SafeSerializableSingleton) in.readObject();
            System.out.println("✓ 对象已从文件反序列化");
        } catch (IOException | ClassNotFoundException e) {
            System.err.println("反序列化失败:" + e.getMessage());
            return;
        }
        
        System.out.println("\n反序列化后实例 hashCode: " + deserialized.hashCode());
        System.out.println("反序列化后实例地址:" + deserialized);
        
        // 验证是否是同一个对象
        System.out.println("\n【验证结果】");
        if (original == deserialized) {
            System.out.println("✓ 是同一个对象 (单例保持成功!)");
        } else {
            System.out.println("✗ 不是同一个对象 (单例被破坏)");
        }
        
        // 清理文件
        new File(fileName).delete();

3、源码之中的应用

3.1、JDK之中的标准单例模式->Runtime类

public class Runtime {

    // 典型的饿汉式
    private static final Runtime currentRuntime = new Runtime();
}

二、工厂模式

1、实现方法

简单工厂:一个工厂 → 一类产品
工厂方法:一个工厂 → 一个产品
抽象工厂:一个工厂 → 一整套产品族

1.1、简单工厂

根据不同的参数创建不同的产品

Image

1.2、工厂方法

根据不同的参数选择不同的工厂,不同的工厂创建不同的产品

Image

1.3、抽象工厂

根据不同的参数选择不同的工厂,工厂创建不同的产品族

Image