单例模式(Singleton Pattern)
- 如何保证线程安全?
- 双重检查锁(DCL)与静态内部类实现的区别?
- 单例模式的优缺点及应用场景。
1. 什么是单例模式
单例模式(Singleton Pattern) 是一种创建型设计模式,它的核心思想是:
一个类在整个系统中只能有一个实例,并且提供一个全局访问点来获取它。
换句话说,单例模式保证了:
- 唯一性:系统中只有一个该类的对象。
- 全局访问:提供一个静态方法获取这个唯一对象。
- 延迟加载(可选):在第一次使用时才创建对象。
2. 原理
单例模式的原理可以用三句话概括:
- 构造方法私有化 → 防止外部通过
new创建对象。 - 在类内部创建唯一实例 → 通过静态变量保存。
- 提供公共静态方法获取实例 → 外部只能通过这个方法访问对象。
3. 流程图
单例模式的执行流程如下:
外部调用 getInstance() ↓ 判断实例是否已创建? ↓ 否 → 创建实例 ↓ 是 → 直接返回实例
4. 单例模式的实现方式
4.1 饿汉式(线程安全,类加载时创建)
java
public class Singleton {
// 1. 在类加载时就创建唯一实例
private static final Singleton instance = new Singleton();
// 2. 构造方法私有化
private Singleton() {}
// 3. 提供全局访问点
public static Singleton getInstance() {
return instance;
}
}
特点:
- 优点:实现简单,线程安全(因为类加载时就创建了实例)。
- 缺点:类加载时就创建对象,可能会浪费内存(如果一直没用到)。
4.2 懒汉式(延迟加载,线程不安全)
java
public class Singleton {
private static Singleton instance;
private Singleton() {}
public static Singleton getInstance() {
if (instance == null) { // 第一次调用时创建
instance = new Singleton();
}
return instance;
}
}
特点:
- 优点:第一次使用时才创建对象,节省内存。
- 缺点:线程不安全,多线程环境下可能会创建多个实例。
4.3 懒汉式 + 线程安全(同步方法)
java
public class Singleton {
private static Singleton instance;
private Singleton() {}
public static synchronized Singleton getInstance() {
if (instance == null) {
instance = new Singleton();
}
return instance;
}
}
特点:
- 优点:线程安全。
- 缺点:
synchronized会影响性能(每次调用都要加锁)。
4.4 双重检查锁(DCL,推荐)
java
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;
}
}
特点:
- 优点:线程安全,性能好(只在第一次创建时加锁)。
- 关键点:
volatile防止指令重排。
4.5 静态内部类(推荐)
java
public class Singleton {
private Singleton() {}
private static class Holder {
private static final Singleton INSTANCE = new Singleton();
}
public static Singleton getInstance() {
return Holder.INSTANCE;
}
}
特点:
- 优点:线程安全,延迟加载,性能好。
- 原理:利用类加载机制,
Holder类只有在第一次调用getInstance()时才会被加载。
5. 使用场景
单例模式适用于:
- 配置类(全局唯一配置对象)
- 日志管理器(全局统一日志记录)
- 线程池(全局唯一线程池对象)
- 数据库连接池(全局唯一连接池)
- 缓存管理器(全局唯一缓存对象)
6. 优缺点
优点:
- 节省内存(只创建一个实例)
- 提供全局访问点
- 控制资源访问(避免多个实例导致冲突)
缺点:
- 单例对象一旦创建,生命周期和应用程序一致,可能占用内存。
- 在多线程环境下实现不当会导致线程安全问题。
- 可能违反单一职责原则(既负责业务逻辑,又负责实例控制)。
7. 示例:日志管理器(静态内部类实现)
java
public class Logger {
private Logger() {}
private static class LoggerHolder {
private static final Logger INSTANCE = new Logger();
}
public static Logger getInstance() {
return LoggerHolder.INSTANCE;
}
public void log(String message) {
System.out.println("[LOG] " + message);
}
public static void main(String[] args) {
Logger logger1 = Logger.getInstance();
Logger logger2 = Logger.getInstance();
logger1.log("系统启动");
logger2.log("用户登录");
System.out.println(logger1 == logger2); // true,说明是同一个实例
}
}
运行结果:
[LOG] 系统启动 [LOG] 用户登录 true
说明 logger1 和 logger2 是同一个对象。
✅ 总结:
- 单例模式的核心是构造方法私有化 + 静态实例 + 全局访问方法。
- 推荐使用静态内部类或**双重检查锁(DCL)**实现,既安全又高效。
工厂模式(Factory Pattern)
- 简单工厂、工厂方法、抽象工厂的区别?
- 在什么场景下选择哪种工厂模式?
1. 什么是工厂模式
工厂模式(Factory Pattern) 是一种创建型设计模式,它的核心思想是:
将对象的创建过程封装起来,通过工厂类来统一管理和创建对象,而不是在代码中直接使用
new。
这样做的好处是:
- 解耦:调用者不需要关心对象的具体创建过程。
- 可扩展:新增产品类型时,只需要修改工厂类,不影响调用者。
2. 原理
工厂模式的原理可以用三句话概括:
- 定义一个工厂类,负责创建对象。
- 调用者通过工厂类获取对象 ,而不是直接
new。 - 工厂类根据条件返回不同的对象实例。
3. 工厂模式的类型
工厂模式有三种常见实现方式:
3.1 简单工厂模式(Simple Factory)
- 一个工厂类,根据传入的参数决定创建哪种对象。
- 缺点:不符合开闭原则(新增产品需要修改工厂类)。
3.2 工厂方法模式(Factory Method)
- 定义一个抽象工厂接口,每个具体工厂负责创建一种产品。
- 优点:符合开闭原则,新增产品只需新增工厂类。
3.3 抽象工厂模式(Abstract Factory)
- 提供一个接口,用于创建一系列相关或依赖的对象。
- 优点:可以创建多个产品族,适合复杂系统。
4. 流程图(以简单工厂为例)
调用者 ↓ 调用工厂类的 createProduct(type) ↓ 工厂类判断 type ↓ 创建对应的产品对象 ↓ 返回给调用者
5. Java 示例
5.1 简单工厂模式
java
// 产品接口
interface Product {
void use();
}
// 具体产品A
class ProductA implements Product {
public void use() {
System.out.println("使用产品A");
}
}
// 具体产品B
class ProductB implements Product {
public void use() {
System.out.println("使用产品B");
}
}
// 工厂类
class SimpleFactory {
public static Product createProduct(String type) {
if ("A".equalsIgnoreCase(type)) {
return new ProductA();
} else if ("B".equalsIgnoreCase(type)) {
return new ProductB();
}
throw new IllegalArgumentException("未知产品类型: " + type);
}
}
// 测试
public class FactoryDemo {
public static void main(String[] args) {
Product p1 = SimpleFactory.createProduct("A");
p1.use();
Product p2 = SimpleFactory.createProduct("B");
p2.use();
}
}
运行结果:
使用产品A 使用产品B
5.2 工厂方法模式
java
// 产品接口
interface Product {
void use();
}
// 具体产品A
class ProductA implements Product {
public void use() {
System.out.println("使用产品A");
}
}
// 具体产品B
class ProductB implements Product {
public void use() {
System.out.println("使用产品B");
}
}
// 工厂接口
interface Factory {
Product createProduct();
}
// 工厂A
class FactoryA implements Factory {
public Product createProduct() {
return new ProductA();
}
}
// 工厂B
class FactoryB implements Factory {
public Product createProduct() {
return new ProductB();
}
}
// 测试
public class FactoryMethodDemo {
public static void main(String[] args) {
Factory factoryA = new FactoryA();
Product p1 = factoryA.createProduct();
p1.use();
Factory factoryB = new FactoryB();
Product p2 = factoryB.createProduct();
p2.use();
}
}
运行结果:
使用产品A 使用产品B
6. 使用场景
工厂模式适用于:
- 对象创建过程复杂(需要很多配置或依赖)。
- 系统需要根据条件创建不同类型的对象。
- 希望解耦对象的创建和使用。
- 需要统一管理对象的创建(比如日志、数据库连接、缓存等)。
7. 优缺点
优点:
- 解耦:调用者不关心对象的创建细节。
- 可维护:统一管理对象创建。
- 可扩展:新增产品类型时,修改或新增工厂类即可。
缺点:
- 简单工厂不符合开闭原则。
- 工厂方法和抽象工厂会增加类的数量,结构更复杂。
8. 总结
- 简单工厂:一个工厂类创建多种产品,结构简单,但扩展性差。
- 工厂方法:每个工厂类只创建一种产品,扩展性好,符合开闭原则。
- 抽象工厂:创建多个产品族,适合复杂系统。
策略模式(Strategy Pattern)
- 如何避免大量
if-else或switch? - 在支付、排序、日志等场景中的应用。
1. 什么是策略模式
策略模式(Strategy Pattern) 是一种行为型设计模式,它的核心思想是:
定义一系列算法,把它们一个个封装起来,并且使它们可以相互替换。策略模式让算法的变化不会影响使用算法的客户。
简单来说:
- 有多个可替换的算法(策略)。
- 客户端可以在运行时选择使用哪一个算法。
- 算法的实现和使用分离,方便扩展。
2. 原理
策略模式的原理可以用三句话概括:
- 定义一个策略接口,声明算法方法。
- 实现多个策略类,每个类封装一种算法。
- 在上下文类中持有策略对象,并在运行时选择具体策略。
3. 流程图
客户端 ↓ 选择具体策略(StrategyA / StrategyB / StrategyC) ↓ 将策略对象传给上下文(Context) ↓ 上下文调用策略对象的方法 ↓ 执行对应的算法
4. Java 示例
4.1 定义策略接口
java
// 策略接口
interface PaymentStrategy {
void pay(int amount);
}
4.2 实现具体策略类
java
// 支付宝支付策略
class AlipayStrategy implements PaymentStrategy {
public void pay(int amount) {
System.out.println("使用支付宝支付 " + amount + " 元");
}
}
// 微信支付策略
class WeChatStrategy implements PaymentStrategy {
public void pay(int amount) {
System.out.println("使用微信支付 " + amount + " 元");
}
}
// 银行卡支付策略
class BankCardStrategy implements PaymentStrategy {
public void pay(int amount) {
System.out.println("使用银行卡支付 " + amount + " 元");
}
}
4.3 上下文类
java
// 上下文类
class PaymentContext {
private PaymentStrategy strategy;
// 构造方法注入策略
public PaymentContext(PaymentStrategy strategy) {
this.strategy = strategy;
}
public void executePayment(int amount) {
strategy.pay(amount);
}
}
4.4 测试
java
public class StrategyPatternDemo {
public static void main(String[] args) {
// 使用支付宝支付
PaymentContext context1 = new PaymentContext(new AlipayStrategy());
context1.executePayment(100);
// 使用微信支付
PaymentContext context2 = new PaymentContext(new WeChatStrategy());
context2.executePayment(200);
// 使用银行卡支付
PaymentContext context3 = new PaymentContext(new BankCardStrategy());
context3.executePayment(300);
}
}
运行结果:
使用支付宝支付 100 元 使用微信支付 200 元 使用银行卡支付 300 元
5. 使用场景
策略模式适用于:
- 系统中有多个算法可以替换(比如不同的排序方法、不同的支付方式)。
- 需要在运行时动态选择算法。
- 避免使用大量的
if-else或switch判断。
6. 优缺点
优点:
- 算法和使用分离,符合开闭原则。
- 可扩展性好,新增策略只需实现接口,不影响原有代码。
- 避免大量条件判断。
缺点:
- 客户端必须知道所有策略类,并自行选择合适的策略。
- 策略类数量可能会增加。
7. 总结
- 策略模式的核心:把算法封装成独立的类,通过接口统一管理。
- 关键点:上下文类持有策略对象,运行时可以替换。
- 适合场景:多种可替换的算法、运行时动态选择。
观察者模式(Observer Pattern)
- JDK中
java.util.Observer的使用。 - 如何实现事件驱动机制?
1. 什么是观察者模式
观察者模式(Observer Pattern) 是一种行为型设计模式,它的核心思想是:
定义对象间的一对多依赖关系,当一个对象状态发生变化时,所有依赖它的对象都会收到通知并自动更新。
简单来说:
- 一个主题(Subject) ,它的状态变化会通知多个观察者(Observer)。
- 观察者可以订阅 或取消订阅主题。
- 主题和观察者之间是松耦合的。
2. 原理
观察者模式的原理可以用三句话概括:
- **主题(Subject)**维护一个观察者列表。
- 当主题状态变化时,调用所有观察者的更新方法。
- 观察者可以随时注册或取消注册。
3. 流程图
主题(Subject) ↑ 注册/取消订阅 观察者(Observer)列表 ↓ 主题状态变化 → 通知所有观察者 → 观察者执行更新逻辑
4. Java 示例
4.1 定义观察者接口
java
// 观察者接口
interface Observer {
void update(String message);
}
4.2 定义主题接口
java
// 主题接口
interface Subject {
void registerObserver(Observer observer);
void removeObserver(Observer observer);
void notifyObservers(String message);
}
4.3 实现具体主题
java
import java.util.ArrayList;
import java.util.List;
class ConcreteSubject implements Subject {
private List<Observer> observers = new ArrayList<>();
@Override
public void registerObserver(Observer observer) {
observers.add(observer);
}
@Override
public void removeObserver(Observer observer) {
observers.remove(observer);
}
@Override
public void notifyObservers(String message) {
for (Observer observer : observers) {
observer.update(message);
}
}
// 主题状态变化时调用
public void changeState(String message) {
System.out.println("主题状态变化: " + message);
notifyObservers(message);
}
}
4.4 实现具体观察者
java
class ConcreteObserverA implements Observer {
@Override
public void update(String message) {
System.out.println("观察者A收到通知: " + message);
}
}
class ConcreteObserverB implements Observer {
@Override
public void update(String message) {
System.out.println("观察者B收到通知: " + message);
}
}
4.5 测试
java
public class ObserverPatternDemo {
public static void main(String[] args) {
ConcreteSubject subject = new ConcreteSubject();
Observer observerA = new ConcreteObserverA();
Observer observerB = new ConcreteObserverB();
// 注册观察者
subject.registerObserver(observerA);
subject.registerObserver(observerB);
// 状态变化
subject.changeState("版本1.0发布");
// 移除一个观察者
subject.removeObserver(observerA);
// 再次状态变化
subject.changeState("版本2.0发布");
}
}
运行结果:
主题状态变化: 版本1.0发布 观察者A收到通知: 版本1.0发布 观察者B收到通知: 版本1.0发布 主题状态变化: 版本2.0发布 观察者B收到通知: 版本2.0发布
5. 使用场景
观察者模式适用于:
- 事件监听机制(GUI按钮点击、鼠标事件等)。
- 消息订阅/发布系统(如新闻推送、微信公众号)。
- 数据变化通知(如股票价格变化通知投资者)。
- 分布式系统中的事件驱动架构。
6. 优缺点
优点:
- 松耦合:主题和观察者之间只依赖接口,不依赖具体实现。
- 可扩展性好:可以随时增加或移除观察者。
- 符合开闭原则:新增观察者不影响主题代码。
缺点:
- 如果观察者数量很多,通知会很耗时。
- 可能出现循环依赖,导致无限通知。
- 观察者收到通知的顺序不可控。
7. 总结
- 核心思想:主题维护观察者列表,状态变化时通知所有观察者。
- 关键点:解耦主题和观察者,方便扩展。
- 常见应用:事件监听、消息推送、数据变化通知。
模板方法模式(Template Method Pattern)
- 抽象类与具体实现类的关系。
- 在Spring框架中的应用案例。
1. 什么是模板方法模式
模板方法模式(Template Method Pattern) 是一种行为型设计模式,它的核心思想是:
在一个抽象类中定义算法的骨架(流程),并将某些步骤的具体实现延迟到子类中。子类可以在不改变算法结构的情况下,重新定义某些步骤的实现。
简单来说:
- 抽象类定义固定的执行流程(模板方法)。
- 流程中的某些步骤由子类实现。
- 保证算法结构不变,但允许子类定制部分行为。
2. 原理
模板方法模式的原理可以用三句话概括:
- 抽象类定义模板方法(final修饰,防止被子类修改流程)。
- 模板方法中调用抽象方法 或钩子方法。
- 子类实现这些抽象方法,完成具体步骤。
3. 流程图
抽象类(AbstractClass) ↓ 模板方法(固定流程) ↓ 调用抽象方法(由子类实现) ↓ 子类(ConcreteClass)实现具体步骤
4. Java 示例
4.1 抽象类定义模板方法
java
abstract class DataProcessor {
// 模板方法(固定流程)
public final void process() {
readData();
processData();
saveData();
}
// 抽象方法(由子类实现)
protected abstract void readData();
protected abstract void processData();
// 具体方法(可选,子类可继承)
protected void saveData() {
System.out.println("保存数据到数据库");
}
}
4.2 子类实现具体步骤
java
class CSVDataProcessor extends DataProcessor {
@Override
protected void readData() {
System.out.println("读取 CSV 文件数据");
}
@Override
protected void processData() {
System.out.println("处理 CSV 数据");
}
}
class ExcelDataProcessor extends DataProcessor {
@Override
protected void readData() {
System.out.println("读取 Excel 文件数据");
}
@Override
protected void processData() {
System.out.println("处理 Excel 数据");
}
}
4.3 测试
java
public class TemplateMethodDemo {
public static void main(String[] args) {
DataProcessor csvProcessor = new CSVDataProcessor();
csvProcessor.process();
System.out.println("----------------");
DataProcessor excelProcessor = new ExcelDataProcessor();
excelProcessor.process();
}
}
运行结果:
读取 CSV 文件数据 处理 CSV 数据 保存数据到数据库 ---------------- 读取 Excel 文件数据 处理 Excel 数据 保存数据到数据库
5. 使用场景
模板方法模式适用于:
- 多个类有相同的处理流程,但某些步骤不同。
- 希望复用流程代码,减少重复。
- 需要保证算法结构不变,但允许子类定制部分行为。
常见应用:
- 数据处理(不同数据源但处理流程一致)。
- 游戏开发(不同关卡的流程一致,但细节不同)。
- Web框架中的请求处理(如 Spring 的
doGet/doPost)。
6. 优缺点
优点:
- 代码复用:公共流程在抽象类中实现,减少重复代码。
- 结构稳定:模板方法固定流程,保证算法结构不变。
- 易扩展:新增子类即可定制不同的步骤。
缺点:
- 继承限制:模板方法依赖继承,灵活性不如组合模式。
- 流程固定:如果流程变化,需要修改抽象类,可能影响所有子类。
7. 总结
- 核心思想:抽象类定义固定流程,子类实现具体步骤。
- 关键点 :模板方法用
final修饰,防止子类修改流程。 - 适合场景:多个类有相同的流程结构,但部分步骤不同。