三招吃透 Java 基础:单例、工厂、模板方法实战,巩固封装、继承和多态
·
三大设计模式:巩固封装、继承和多态
本文档针对 Java 新手,基于封装、继承、多态三大特性,详细讲解单例模式、工厂模式、模板方法模式,重点补充单例模式的饿汉式、懒汉式实现细节,所有示例均为可直接运行的 Java 代码。
一、单例模式(Singleton Pattern)
1. 核心定义
单例模式是创建型设计模式,核心目标是:保证一个类在程序运行期间仅存在一个实例对象,并提供一个全局统一的访问入口,避免重复创建对象造成的资源浪费。
2. 核心设计思路
- 私有化构造方法:禁止外部通过
new关键字创建实例; - 类内部自行创建实例:通过静态属性存储唯一实例;
- 提供公共静态方法:返回该唯一实例,作为全局访问点。
3. 常见实现方式
1. 饿汉式(立即加载)
核心特点
- 类加载时(ClassLoader 加载阶段)就创建实例,属于 “立即初始化”;
- 天然线程安全(JVM 保证类加载过程是线程安全的);
- 简单易实现,但可能造成资源浪费(即使实例未被使用,也会占用内存)。
完整代码
/**
* 单例模式 - 饿汉式(基础版)
* 优点:线程安全、实现简单;缺点:类加载即初始化,可能浪费内存
*/
public class HungrySingleton {
// 1. 私有化静态实例:类加载时直接初始化,JVM 保证线程安全
private static final HungrySingleton INSTANCE = new HungrySingleton();
// 2. 私有化构造方法:禁止外部通过 new 创建对象
private HungrySingleton() {
// 防止反射破坏单例(可选,新手可先忽略)
if (INSTANCE != null) {
throw new RuntimeException("禁止创建多个单例实例");
}
}
// 3. 公共静态方法:返回唯一实例
public static HungrySingleton getInstance() {
return INSTANCE;
}
// 测试方法:验证实例唯一性
public void showInfo() {
System.out.println("饿汉式单例实例的哈希值:" + this.hashCode());
}
// 测试主方法
public static void main(String[] args) {
// 获取两个实例
HungrySingleton s1 = HungrySingleton.getInstance();
HungrySingleton s2 = HungrySingleton.getInstance();
// 验证是否为同一个对象(哈希值相同,== 结果为 true)
s1.showInfo();
s2.showInfo();
System.out.println("s1 和 s2 是否为同一个实例:" + (s1 == s2)); // 输出 true
}
}
2. 懒汉式(延迟加载)
核心特点
- 实例在首次调用
getInstance()方法时才创建,属于 “延迟初始化”; - 节省内存(按需创建),但需要手动保证线程安全;
- 新手需重点掌握 “基础懒汉式” 和 “线程安全懒汉式” 的区别。
① 基础懒汉式(非线程安全)
/**
* 单例模式 - 懒汉式(基础版,非线程安全)
* 优点:延迟加载、节省内存;缺点:多线程环境下会创建多个实例
*/
public class LazySingletonUnsafe {
// 1. 私有化静态实例:初始为 null,延迟初始化
//此时就不能像饿汉式那样在这里用final了,因为下面还要赋值
private static LazySingletonUnsafe INSTANCE = null;
// 2. 私有化构造方法
private LazySingletonUnsafe() {}
// 3. 公共静态方法:首次调用时创建实例
public static LazySingletonUnsafe getInstance() {
// 问题:多线程同时进入 if 判断,会创建多个实例
if (INSTANCE == null) {
INSTANCE = new LazySingletonUnsafe();
}
return INSTANCE;
}
// 测试方法
public void showInfo() {
System.out.println("非线程安全懒汉式实例哈希值:" + this.hashCode());
}
// 多线程测试(验证线程不安全)
public static void main(String[] args) {
// 开启 10 个线程,同时获取实例
for (int i = 0; i < 10; i++) {
new Thread(() -> {
LazySingletonUnsafe instance = LazySingletonUnsafe.getInstance();
instance.showInfo();
}).start();
}
// 运行结果:会出现多个不同的哈希值,说明创建了多个实例
}
}
② 线程安全懒汉式(加锁版)
/**
* 单例模式 - 懒汉式(线程安全版,加 synchronized 锁)
* 优点:延迟加载、线程安全;缺点:加锁导致性能损耗(每次调用都要抢锁)
*/
public class LazySingletonSafe {
private static LazySingletonSafe INSTANCE = null;
private LazySingletonSafe() {}
// 给方法加 synchronized 锁,保证同一时间只有一个线程能执行
public static synchronized LazySingletonSafe getInstance() {
if (INSTANCE == null) {
INSTANCE = new LazySingletonSafe();
}
return INSTANCE;
}
public void showInfo() {
System.out.println("线程安全懒汉式实例哈希值:" + this.hashCode());
}
// 多线程测试(验证线程安全)
public static void main(String[] args) {
for (int i = 0; i < 10; i++) {
new Thread(() -> {
LazySingletonSafe instance = LazySingletonSafe.getInstance();
instance.showInfo();
}).start();
}
// 运行结果:所有哈希值相同,仅创建一个实例
}
}
③ 双重检查锁(DCL,最优懒汉式)
/**
* 单例模式 - 懒汉式(双重检查锁 DCL,新手进阶版)
* 优点:延迟加载、线程安全、性能优(仅首次创建时加锁);
* 注意:INSTANCE 必须加 volatile 关键字,防止指令重排序
*/
public class LazySingletonDCL {
// volatile 关键字:禁止指令重排序,保证 INSTANCE 初始化完成后才被读取
private static volatile LazySingletonDCL INSTANCE = null;
private LazySingletonDCL() {}
public static LazySingletonDCL getInstance() {
// 第一次检查:实例已创建则直接返回,避免每次抢锁
if (INSTANCE == null) {
// 加锁:保证同一时间只有一个线程进入
synchronized (LazySingletonDCL.class) {
// 第二次检查:防止多个线程等待锁后重复创建
if (INSTANCE == null) {
INSTANCE = new LazySingletonDCL();
}
}
}
return INSTANCE;
}
public void showInfo() {
System.out.println("DCL 懒汉式实例哈希值:" + this.hashCode());
}
public static void main(String[] args) {
for (int i = 0; i < 10; i++) {
new Thread(() -> {
LazySingletonDCL instance = LazySingletonDCL.getInstance();
instance.showInfo();
}).start();
}
}
}
4. 饿汉式 vs 懒汉式 对比
| 特性 | 饿汉式 | 懒汉式(DCL) |
|---|---|---|
| 初始化时机 | 类加载时 | 首次调用 getInstance () |
| 线程安全 | 天然安全(JVM 保证) | 手动加锁(DCL)保证 |
| 内存占用 | 可能浪费(提前初始化) | 按需加载,节省内存 |
| 性能 | 无锁,性能优 | 首次创建加锁,后续无锁 |
| 实现复杂度 | 简单(新手首选) | 稍复杂(需注意 volatile) |
5. 适用场景
- 配置管理类(如数据库配置、系统参数配置):全局唯一配置,避免重复加载;
- 日志工具类:保证日志写入顺序,避免多实例导致日志混乱;
- 数据库连接池:减少连接创建开销,统一管理连接资源。
二、工厂模式(Factory Pattern)
1. 核心定义
工厂模式是创建型设计模式,核心目标是:将对象的创建逻辑从业务代码中抽离,封装到专门的 “工厂类” 中,外部只需通过工厂获取实例,无需关心创建细节,核心体现 “多态” 和 “封装”。
2. 核心设计思路
- 定义产品接口 / 抽象类:统一所有产品的行为规范;
- 实现具体产品类:不同产品实现接口,体现多态;
- 定义工厂类:封装对象创建逻辑,根据入参 / 规则返回对应产品实例。
3. 分类与实现
1. 简单工厂模式(静态工厂,新手首选)
核心特点:一个工厂类创建所有产品,无需创建工厂实例,通过静态方法调用。
// 步骤 1:定义产品接口(统一行为)
public interface Shape {
void draw(); // 所有图形都有“绘制”行为
}
// 步骤 2:实现具体产品类
// 圆形
public class Circle implements Shape {
@Override
public void draw() {
System.out.println("绘制圆形");
}
}
// 矩形
public class Rectangle implements Shape {
@Override
public void draw() {
System.out.println("绘制矩形");
}
}
// 步骤 3:创建简单工厂类
public class ShapeFactory {
// 静态方法:根据类型创建对应图形
public static Shape createShape(String shapeType) {
// 封装创建逻辑,外部无需知道 new Circle() 还是 new Rectangle()
switch (shapeType) {
case "circle":
return new Circle();
case "rectangle":
return new Rectangle();
default:
throw new IllegalArgumentException("不支持的图形类型:" + shapeType);
}
}
}
// 步骤 4:测试代码
public class SimpleFactoryTest {
public static void main(String[] args) {
// 外部只需调用工厂方法,无需直接 new 产品类
Shape circle = ShapeFactory.createShape("circle");
circle.draw(); // 输出:绘制圆形
Shape rectangle = ShapeFactory.createShape("rectangle");
rectangle.draw(); // 输出:绘制矩形
}
}
2. 工厂方法模式(进阶版)
核心特点:为每个产品创建专属工厂,符合 “开闭原则”(新增产品只需加工厂,无需改原有代码)。
// 步骤 1:复用上述 Shape 接口和 Circle、Rectangle 产品类
// 步骤 2:定义工厂接口
public interface ShapeFactory {
Shape createShape(); // 工厂方法:创建对应产品
}
// 步骤 3:实现具体工厂类
// 圆形工厂
public class CircleFactory implements ShapeFactory {
@Override
public Shape createShape() {
return new Circle();
}
}
// 矩形工厂
public class RectangleFactory implements ShapeFactory {
@Override
public Shape createShape() {
return new Rectangle();
}
}
// 步骤 4:测试代码
public class FactoryMethodTest {
public static void main(String[] args) {
// 创建圆形工厂 → 获取圆形实例
ShapeFactory circleFactory = new CircleFactory();
Shape circle = circleFactory.createShape();
circle.draw();
// 创建矩形工厂 → 获取矩形实例
ShapeFactory rectangleFactory = new RectangleFactory();
Shape rectangle = rectangleFactory.createShape();
rectangle.draw();
// 新增三角形:只需加 Triangle 产品类 + TriangleFactory 工厂类,无需改原有代码
}
}
4. 核心优缺点
| 优点 | 缺点 |
|---|---|
| 解耦:产品创建与使用分离 | 简单工厂模式:新增产品需修改工厂类(违反开闭原则) |
| 多态:统一接口调用,无需区分产品类型 | 工厂方法模式:类数量翻倍(产品 + 工厂),增加复杂度 |
| 便于扩展:新增产品只需加类(工厂方法) | 简单工厂模式:创建逻辑集中,复杂后不易维护 |
5. 适用场景
- 产品类型较多且创建逻辑复杂(如支付方式:支付宝、微信、银联);
- 外部无需知道产品创建细节(如数据库驱动创建,JDBC 用工厂模式隐藏不同数据库的驱动实现);
- 需统一管理产品创建(如校验参数、初始化配置后再创建实例)。
三、模板方法模式(Template Method Pattern)
1. 核心定义
模板方法模式是行为型设计模式,核心目标是:在抽象父类中定义一个算法的 “骨架(模板)”,将算法中可变的步骤延迟到子类实现,固定步骤留在父类,核心体现 “继承” 和 “多态” 的结合。
2. 核心设计思路
- 抽象父类:定义算法的固定流程(模板方法,通常用
final修饰,防止子类修改流程); - 抽象方法:父类声明可变步骤,由子类实现;
- 具体子类:重写抽象方法,定制化可变步骤,固定流程不变。
3. 完整实现
// 步骤 1:定义抽象父类(模板类)
public abstract class AbstractOrderProcessor {
/**
* 模板方法:订单处理的固定流程(final 修饰,禁止子类修改)
* 流程:校验订单 → 扣减库存 → 生成订单 → 通知用户
*/
public final void processOrder() {
validateOrder(); // 固定步骤 1:校验订单(父类实现)
deductStock(); // 可变步骤 1:扣减库存(子类实现)
generateOrder(); // 固定步骤 2:生成订单(父类实现)
notifyUser(); // 可变步骤 2:通知用户(子类实现)
}
// 固定方法:校验订单(所有订单校验逻辑相同)
private void validateOrder() {
System.out.println("1. 校验订单:订单号、金额、收货地址合规");
}
// 固定方法:生成订单(所有订单生成逻辑相同)
private void generateOrder() {
System.out.println("3. 生成订单:创建订单记录,分配订单号");
}
// 抽象方法:扣减库存(不同订单扣减逻辑不同,子类实现)
protected abstract void deductStock();
// 抽象方法:通知用户(不同订单通知方式不同,子类实现)
protected abstract void notifyUser();
}
// 步骤 2:实现子类(普通商品订单)
public class NormalOrderProcessor extends AbstractOrderProcessor {
@Override
protected void deductStock() {
System.out.println("2. 扣减普通商品库存:直接扣减仓库库存");
}
@Override
protected void notifyUser() {
System.out.println("4. 通知用户:发送短信告知订单创建成功");
}
}
// 步骤 3:实现子类(预售商品订单)
public class PreSaleOrderProcessor extends AbstractOrderProcessor {
@Override
protected void deductStock() {
System.out.println("2. 扣减预售商品库存:扣减预售库存,锁定货源");
}
@Override
protected void notifyUser() {
System.out.println("4. 通知用户:发送APP推送告知预售订单创建成功");
}
}
// 步骤 4:测试代码
public class TemplateMethodTest {
public static void main(String[] args) {
// 处理普通订单
System.out.println("=== 处理普通订单 ===");
AbstractOrderProcessor normalOrder = new NormalOrderProcessor();
normalOrder.processOrder();
// 处理预售订单
System.out.println("\n=== 处理预售订单 ===");
AbstractOrderProcessor preSaleOrder = new PreSaleOrderProcessor();
preSaleOrder.processOrder();
}
}
4. 代码运行结果
=== 处理普通订单 ===
1. 校验订单:订单号、金额、收货地址合规
2. 扣减普通商品库存:直接扣减仓库库存
3. 生成订单:创建订单记录,分配订单号
4. 通知用户:发送短信告知订单创建成功
=== 处理预售订单 ===
1. 校验订单:订单号、金额、收货地址合规
2. 扣减预售商品库存:扣减预售库存,锁定货源
3. 生成订单:创建订单记录,分配订单号
4. 通知用户:发送APP推送告知预售订单创建成功
5. 核心优缺点
| 优点 | 缺点 |
|---|---|
| 代码复用:父类封装固定流程,子类只需实现可变部分 | 算法骨架固定,修改流程需改父类(违反开闭原则) |
| 开闭原则:新增订单类型只需加子类,无需改父类 | 子类过多时,增加系统复杂度 |
| 统一流程:保证所有子类遵循相同的算法步骤 | 父类依赖子类实现,可能导致耦合过高 |
6. 适用场景
- 多个业务逻辑流程基本一致,仅部分步骤有差异(如:不同支付方式的流程、不同报表的生成流程);
- 希望限制子类对核心流程的修改,仅开放细节定制(如:框架中的钩子方法);
- 典型应用:Spring 框架中的
JdbcTemplate(固定数据库操作流程,开放 SQL 执行、结果处理等可变步骤)。
四、总结
- 单例模式:核心是 “唯一实例”,新手优先掌握饿汉式(简单安全),进阶学习 DCL 懒汉式(性能优),
volatile关键字是 DCL 的关键; - 工厂模式:核心是 “封装对象创建”,简单工厂(静态方法)适合新手,工厂方法模式更符合开闭原则,核心体现多态;
- 模板方法模式:核心是 “固定流程 + 可变步骤”,父类用
final模板方法固定流程,子类重写抽象方法定制细节,核心体现继承和多态的结合。
这三个模式是 Java 面向对象编程的核心落地场景,建议先跑通所有代码示例,再尝试在小项目(如图书管理系统)中应用,加深对封装、继承、多态的理解。
更多推荐



所有评论(0)