1. 单例模式的六种写法?推荐哪种?

  1. 饿汉式:static final 直接 new。类加载即创建。简单、线程安全,但可能浪费(没用到也创建)
  2. 懒汉式(非线程安全):getInstance 时 new。多线程会创建多个 ❌
  3. 懒汉式(同步方法):getInstance 加 synchronized。线程安全但每次调用加锁,性能差
  4. 双重检查锁(DCL):volatile + 两次判空 + synchronized 代码块(详见并发模块)
  5. 静态内部类(推荐之一):JVM 类加载机制天然懒加载 + 线程安全
  6. 枚举(最推荐):天然单例 + 防反射 + 防序列化破坏,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. 简单工厂、工厂方法、抽象工厂的区别?

工厂方法模式骨架
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)
  • 对比:工厂模式管"创建哪类",建造者管"怎么一步步组装"

🎤 常见面试追问

  1. 单例模式哪种写法最好?为什么?——枚举最稳(天然防反射/序列化/多实例);静态内部类(Holder)懒加载+线程安全也常用。DCL 要 volatile。
  2. 反射和序列化怎么破坏单例?——反射 setAccessible 调私有构造器能 new 第二个;反序列化不走构造器直接建新对象。防御:枚举天然免疫;或 readResolve() 返回原实例。
  3. 简单工厂、工厂方法、抽象工厂的区别?——简单工厂:一个类 switch 创建(不算 GoF);工厂方法:每个产品一个工厂子类(扩展对,符合开闭);抽象工厂:一个工厂生产一族配套产品。
  4. Builder 和构造器重载的取舍?——参数多(≥4 且多数可选)→ Builder(可读、可校验、不可变);参数少 → 构造器 + 重载。
  5. Spring 里工厂模式在哪?——BeanFactory 就是工厂:按 BeanDefinition"配方"创建对象;FactoryBean 是定制创建逻辑的接口。

📖 名词解释(本页术语)

术语 大白话解释
单例模式保证一个类全局只有一个实例(私有构造 + 静态获取)。配置类、连接池用它。写法:饿汉/懒汉/DCL/静态内部类/枚举。
工厂模式不直接 new,交给"工厂"按条件创建对象——创建逻辑集中管理,解耦调用方与实现类。
抽象工厂工厂接口定义"一族"产品的创建(如 Mac 工厂产键盘+鼠标),保证产品配套。
建造者模式(Builder)分步设置参数最后 build() 生成对象(链式调用),适合多可选参数。Lombok @Builder 就是它。
原型模式(Prototype)复制现有对象创建新对象(clone),避免重新初始化。浅拷贝只复制引用,深拷贝要递归复制(或序列化);clone() 不调用构造器,所以单例不能实现 Cloneable。Spring 的 prototype 作用域同名不同义(每用新实例,不是克隆)。
DCL(双重检查锁)单例写法:两次判空 + synchronized + volatile(防重排导致拿到半构造对象)。
⚠️ 本页面由 AI 生成,内容仅供参考,请以官方文档和实际源码为准。