🏭 创建型模式
单例(全写法)· 工厂/抽象工厂 · 建造者 · 原型
1. 单例模式的六种写法?推荐哪种?
- 饿汉式:static final 直接 new。类加载即创建。简单、线程安全,但可能浪费(没用到也创建)
- 懒汉式(非线程安全):getInstance 时 new。多线程会创建多个 ❌
- 懒汉式(同步方法):getInstance 加 synchronized。线程安全但每次调用加锁,性能差
- 双重检查锁(DCL):volatile + 两次判空 + synchronized 代码块(详见并发模块)
- 静态内部类(推荐之一):JVM 类加载机制天然懒加载 + 线程安全
- 枚举(最推荐):天然单例 + 防反射 + 防序列化破坏,Effective Java 作者背书
静态内部类 & 枚举
// 静态内部类:懒加载 + 线程安全(JVM 保证类初始化加锁)
class Singleton {
private Singleton() {}
private static class Holder { private static final Singleton INSTANCE = new Singleton(); }
public static Singleton getInstance() { return Holder.INSTANCE; }
}
// 枚举:最简单,防反射/序列化破坏
enum Singleton { INSTANCE; }
🎯 面试要点
- 能答出"反射和序列化如何破坏单例 + 枚举为何免疫"是深度分(见高频题页)
- Spring 的单例是"容器级单例",非 JVM 级(每容器一个),别混淆
2. 简单工厂、工厂方法、抽象工厂的区别?
- 简单工厂:一个工厂类,switch 类型返回不同产品。不算 GoF 模式(只是封装)。不符合开闭原则
- 工厂方法:工厂抽象类 + 子类各自创建对应产品——新增产品要新增工厂子类(扩展对,修改不对 → 符合开闭)
- 抽象工厂:工厂接口定义一组产品族的创建(如"Mac 工厂"生产键盘+鼠标;"Win 工厂"生产另一套),解决产品族配套问题
工厂方法模式骨架
interface Logger { void log(String msg); }
interface LoggerFactory { Logger createLogger(); } // 工厂方法
class FileLoggerFactory implements LoggerFactory {
public Logger createLogger() { return new FileLogger(); }
}
// 使用方只依赖 LoggerFactory/Logger 抽象,新增数据库日志 = 新工厂 + 新实现,不改旧代码
🎯 面试要点
- 框架里全是工厂:Spring 的 BeanFactory(按 BeanDefinition 创建)、MyBatis 的 SqlSessionFactory、JDBC DriverManager
- 识别口诀:简单工厂"一个类管创建",工厂方法"每个产品一个工厂",抽象工厂"工厂生产一组配套产品"
3. 建造者模式(Builder)解决什么问题?
问题:对象有大量可选参数时,构造器重载爆炸(telescoping constructor),或需要分步构建校验。
解决:Builder 对象逐步设置参数,最后 build() 生成目标对象。经典案例:Lombok @Builder、StringBuilder、Spring 的 UriComponentsBuilder、MyBatis 的 SqlSessionFactoryBuilder。
Builder 模式(对比构造器重载)
class User {
private final String name; // 必填
private final int age; // 可选
private final String email; // 可选
private User(Builder b) { name = b.name; age = b.age; email = b.email; }
public static class Builder {
private final String name; // 必填放构造
private int age; private String email;
public Builder(String name) { this.name = name; }
public Builder age(int a) { age = a; return this; } // 链式
public Builder email(String e) { email = e; return this; }
public User build() { return new User(this); }
}
}
// 使用:new User.Builder("张三").age(20).email("z@x.com").build()
🎯 面试要点
- Builder 常用于不可变对象 + 多可选参数(配合 final + Lombok)
- 对比:工厂模式管"创建哪类",建造者管"怎么一步步组装"