一、单例模式:从"饿汉"到"枚举"的演进
面试题:请简述单例模式的几种实现方式及优缺点。
单例模式确保一个类只有一个实例,并提供一个全局访问点。它在日志、配置、线程池等场景中广泛使用。
1. 饿汉式(线程安全,但可能浪费内存)
java
public class Singleton {
private static final Singleton instance = new Singleton();
private Singleton() {}
public static Singleton getInstance() {
return instance;
}
}
-
优点:类加载时完成初始化,天然线程安全。
-
缺点:即使从未使用,也会创建实例,造成内存浪费。
2. 懒汉式(双重检查锁,需volatile)
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防止指令重排。
3. 静态内部类(推荐写法)
java
public class Singleton {
private Singleton() {}
private static class Holder {
private static final Singleton INSTANCE = new Singleton();
}
public static Singleton getInstance() {
return Holder.INSTANCE;
}
}
-
优点:延迟加载 + 线程安全(由类加载机制保证),代码简洁。
-
适用:大部分生产环境推荐使用。
4. 枚举(最安全)
java
public enum Singleton {
INSTANCE;
public void doSomething() { }
}
-
优点:天然防止反射攻击和序列化破坏。
-
缺点:不够灵活(如无法延迟加载参数化实例)。
-
适用:对安全性要求极高的场景(如框架底层)。
✅ 小结:日常开发推荐静态内部类 ,框架或安全敏感场景推荐枚举。
二、工厂模式 vs 抽象工厂模式:从"一类产品"到"产品族"
面试题:工厂模式和抽象工厂模式的区别?
两者都属于创建型模式 ,核心区别在于产品维度。
1. 工厂模式(Factory Method)
定义:定义一个创建对象的接口,但由子类决定实例化哪一个类。
特点 :生产同一类产品(同一等级结构)。
示例:生产不同品牌的车
java
interface Car { void run(); }
class Benz implements Car { ... }
class BMW implements Car { ... }
interface CarFactory {
Car createCar();
}
class BenzFactory implements CarFactory {
public Car createCar() { return new Benz(); }
}
2. 抽象工厂模式(Abstract Factory)
定义:创建一系列相关或相互依赖的产品族,而无需指定具体类。
特点 :生产多个等级的产品(产品族)。
示例:生产"高端车"和"低端车"两个产品族,每个族包含引擎和座椅
java
interface Engine { ... }
class HighEndEngine implements Engine { ... }
class LowEndEngine implements Engine { ... }
interface Seat { ... }
class HighEndSeat implements Seat { ... }
class LowEndSeat implements Seat { ... }
interface CarFactory {
Engine createEngine();
Seat createSeat();
}
class HighEndFactory implements CarFactory {
public Engine createEngine() { return new HighEndEngine(); }
public Seat createSeat() { return new HighEndSeat(); }
}
对比总结
| 维度 | 工厂模式 | 抽象工厂模式 |
|---|---|---|
| 产品数量 | 单一产品 | 多个产品(产品族) |
| 扩展产品族 | 不支持(需改代码) | 支持新增具体工厂 |
| 扩展产品等级 | 支持新增子类 | 较难(需改接口) |
| 复杂度 | 低 | 高 |
✅ 一句话区分:工厂模式 针对一个产品等级结构,抽象工厂针对多个产品等级结构(产品族)。
三、多态:面向对象的灵魂
面试题:说说你对"多态"的理解,并举例说明其好处。
1. 多态的本质
多态 是指同一行为具有多个不同表现形式。在Java中通过继承 + 方法重写 + 父类引用指向子类对象实现。
java
Animal a = new Dog(); // 父类引用指向子类对象
a.eat(); // 实际执行的是 Dog 的 eat 方法(动态绑定)
2. 多态的三大前提
-
继承或实现(extends/implements)
-
方法重写
-
父类/接口引用指向子类对象
3. 实际好处:提高扩展性与维护性
反例(不使用多态):
java
void feed(Dog dog) { dog.eat(); }
void feed(Cat cat) { cat.eat(); }
// 每增加一种动物,就需要新增一个方法
使用多态:
java
void feed(Animal animal) {
animal.eat(); // 任何继承Animal的子类都可以传入
}
4. 经典应用场景
-
集合框架 :
List<String> list = new ArrayList<>(); -
Spring依赖注入:面向接口编程,替换实现类无需改动调用方
-
策略模式:上下文持有策略接口,运行时切换具体算法
✅ 多态的核心价值:面向抽象编程,而非面向具体实现编程 ------ 这正是依赖倒置原则的体现。
总结与延伸
| 知识点 | 核心关键词 | 面试常见追问 |
|---|---|---|
| 单例模式 | 饿汉、懒汉、静态内部类、枚举 | 如何防止反射破坏?如何实现有参构造的单例? |
| 工厂模式 vs 抽象工厂 | 单一产品 vs 产品族 | 在实际框架(如Spring)中哪里体现了工厂模式? |
| 多态 | 动态绑定、父类引用、可扩展性 | 重载是编译期多态还是运行期多态?多态与接口隔离原则的关系? |
给面试者的实战建议
-
画图:准备在白板上画出工厂模式的类结构图。
-
结合项目:讲一个你实际使用单例(如Redis连接池)或工厂模式(如支付渠道工厂)的例子。
-
对比意识:面试官问"为什么用静态内部类而不是饿汉式",要能说出内存与性能的权衡。
面向对象与设计模式不是"八股文",而是解决复杂工程问题的思维工具。理解其背后的设计原则(开闭、里氏替换、依赖倒置),才能真正写出优雅、可维护的代码。