创建型设计模式:关注对象的创建过程 单例模式、工厂方法模式、抽象工厂模式、建造者模式、原型模式
一、单例模式
表示全局唯一 处理资源访问冲突
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、双重检查锁
既支持延迟加载、又支持高并发的单例实现方式;
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、简单工厂
根据不同的参数创建不同的产品
1.2、工厂方法
根据不同的参数选择不同的工厂,不同的工厂创建不同的产品
1.3、抽象工厂
根据不同的参数选择不同的工厂,工厂创建不同的产品族