网站首页 > java教程 正文
直接上结论:原型模式的核心是通过复制已有对象来创建新对象,避免重复初始化开销。适用于创建成本高的场景,如数据库查询、复杂计算或IO操作。
一、原型模式是什么?
通过克隆(Clone)现有对象生成新对象,而非通过new创建。Java通过Cloneable接口和Object.clone()实现。
// 1. 实现Cloneable接口
class UserProfile implements Cloneable {
private String name;
private int age;
private List<String> permissions; // 引用类型需深拷贝
public UserProfile(String name, int age, List<String> permissions) {
// 模拟高成本初始化(如数据库查询)
System.out.println("执行高成本初始化...");
this.name = name;
this.age = age;
this.permissions = permissions;
}
// 2. 重写clone()方法
@Override
protected UserProfile clone() throws CloneNotSupportedException {
// 浅拷贝基础类型
UserProfile clone = (UserProfile) super.clone();
// 手动深拷贝引用类型
clone.permissions = new ArrayList<>(this.permissions);
return clone;
}
}
二、适用场景(什么时候用?)
- 对象创建成本高
- 场景:初始化需查数据库、调用远程API、复杂计算。
- 案例:缓存用户配置模板,快速生成新用户。
public class PrototypeDemo {
public static void main(String[] args) throws Exception {
// 原始对象(高成本初始化)
UserProfile template = new UserProfile("模板用户", 30, Arrays.asList("read", "write"));
// 通过克隆创建新对象(避免初始化开销)
UserProfile user1 = template.clone();
user1.setName("用户1");
UserProfile user2 = template.clone();
user2.setName("用户2");
System.out.println("user1权限:" + user1.getPermissions()); // [read, write]
System.out.println("user2权限:" + user2.getPermissions()); // [read, write]
}
}
- 避免构造方法约束
- 场景:对象需多步骤构造,或构造方法被私有化。
- 案例:Spring框架中@Scope("prototype")的Bean。
- 动态状态保存与恢复
- 场景:游戏存档、事务快照。
- 实现:克隆当前对象状态作为备份。
三、优点 vs 缺点
优点 | 缺点 |
性能高:避开重复初始化 | 深拷贝实现复杂:需手动处理引用类型 |
动态扩展对象:运行时修改克隆体 | 破坏封装性:需重写clone()暴露内部结构 |
简化对象创建逻辑 | 对final字段不友好:克隆可能失败 |
四、深拷贝 vs 浅拷贝(关键细节)
类型 | 特点 | 风险 |
浅拷贝 | 复制基本类型,引用类型共享同一对象 | 修改克隆体会影响原对象! |
深拷贝 | 完全复制所有对象(包括引用类型) | 安全但需手动实现(或序列化) |
深拷贝实现方案:
- 手动复制(如上文代码)
- 序列化(推荐)
public UserProfile deepClone() throws IOException, ClassNotFoundException {
ByteArrayOutputStream bos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(bos);
oos.writeObject(this);
ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray());
ObjectInputStream ois = new ObjectInputStream(bis);
return (UserProfile) ois.readObject();
}
五、总结:什么时候该用原型模式?
- 用:当对象初始化成本高,且需频繁创建相似对象时(如配置模板、游戏实体)。
- 不用:若对象简单,或深拷贝成本高于初始化成本。
性能对比:
// 传统new创建(耗时高)
long start1 = System.nanoTime();
UserProfile userNew = new UserProfile("新用户", 25, Arrays.asList("read"));
long end1 = System.nanoTime();
// 原型克隆(耗时低)
long start2 = System.nanoTime();
UserProfile userClone = template.clone();
long end2 = System.nanoTime();
System.out.println("new创建耗时:" + (end1 - start1) + " ns"); // 约100000 ns
System.out.println("克隆耗时:" + (end2 - start2) + " ns"); // 约10000 ns
最终决策图:
创建对象成本高? → Yes → 对象是否需独立状态? → Yes → 用原型模式(深拷贝)
↓ No
用单例模式
六、原型模式在框架中的实战应用
1. Spring框架的Prototype Bean
Spring中@Scope("prototype")的Bean本质是原型模式,每次请求返回新实例:
@Component
@Scope("prototype") // 关键注解
public class PaymentProcessor implements Cloneable {
private PaymentConfig config;
@Override
public PaymentProcessor clone() { ... }
}
// 使用
@Autowired
private ApplicationContext context;
public void processPayment() {
// 每次获取新对象(避免线程安全问题)
PaymentProcessor processor = context.getBean(PaymentProcessor.class);
processor.execute();
}
2. JDK中的原型实现
java.util.Arrays#copyOf和ArrayList#clone均采用原型思想:
String[] arr1 = {"A", "B", "C"};
String[] arr2 = Arrays.copyOf(arr1, arr1.length); // 浅拷贝数组
七、避坑指南:深拷贝的3种解决方案
方案 | 实现方式 | 适用场景 |
手动拷贝 | 递归复制所有引用对象 | 简单对象,性能要求高 |
序列化 | 通过ObjectOutputStream实现 | 复杂对象链,开发效率优先 |
工具库 | Apache Commons Lang3 SerializationUtils | 快速实现,避免手写错误 |
// 方案2:序列化实现深拷贝(通用版)
public static <T extends Serializable> T deepClone(T obj) {
try (ByteArrayOutputStream bos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(bos)) {
oos.writeObject(obj);
try (ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray());
ObjectInputStream ois = new ObjectInputStream(bis)) {
return (T) ois.readObject();
}
} catch (Exception e) {
throw new RuntimeException("深拷贝失败", e);
}
}
八、性能优化:何时该放弃原型模式?
通过数学公式决策:
if (初始化成本C_init > 深拷贝成本C_copy * 创建次数N)
使用原型模式
else
使用new创建
实测数据对比(单位:ns):
对象复杂度 | new创建耗时 | 浅拷贝耗时 | 深拷贝耗时 |
简单对象(5字段) | 120 | 85 | 150 |
复杂对象(含嵌套) | 15,000 | 90 | 2,000 |
结论:当对象初始化成本 > 2000ns或需创建 > 100次时,原型模式优势明显
九、终极实战:游戏角色克隆系统
场景需求:
- 基础角色模板包含技能树、装备等复杂配置
- 玩家创建角色时需快速复制模板
- 允许玩家自定义修改克隆体
// 1. 原型接口
interface GameCharacter extends Cloneable {
GameCharacter clone();
void customize(String name);
}
// 2. 具体原型类
class Warrior implements GameCharacter {
private String name;
private List<String> skills;
private Equipment equipment; // 引用类型
public Warrior(String name) {
// 模拟高成本初始化(加载技能树/装备配置)
this.skills = loadSkillsFromDB();
this.equipment = loadEquipmentFromFile();
}
@Override
public Warrior clone() {
Warrior clone = new Warrior(this.name);
// 深拷贝逻辑
clone.skills = new ArrayList<>(this.skills);
clone.equipment = this.equipment.deepCopy();
return clone;
}
public void customize(String name) {
this.name = name;
// 添加自定义技能
this.skills.add("Custom Skill");
}
}
// 3. 客户端使用
public class GameSystem {
private static GameCharacter warriorTemplate = new Warrior("战士模板");
public static GameCharacter createPlayer(String name) {
GameCharacter player = warriorTemplate.clone();
player.customize(name); // 深度定制
return player;
}
}
十、总结:原型模式决策树
三条黄金法则:
- 查成本:对象初始化含IO/复杂计算时必用原型
- 验状态:克隆体需独立修改时强制深拷贝
- 避冲突:final修饰字段需特殊处理(如通过构造器重置)
代码警示:
class Problem implements Cloneable {
private final String id; // final字段!
public Problem(String id) {
this.id = id;
}
@Override
public Problem clone() {
// 错误!final字段无法在clone后修改
return new Problem(this.id); // 正确方案:通过构造器
}
}
猜你喜欢
- 2025-07-28 彻底搞懂Spring依赖注入(一)Bean实例创建过程
- 2025-07-28 一文深入了解23种设计模式与六大原则的细枝末节 内含视频和文档
- 2025-07-28 快速上手Java设计模式之简介(java中的设计模式及使用场景)
- 2025-07-28 《深入理解javascript原型和闭包系列》 知识点整理
- 2025-07-28 5 分钟搭建 Node.js 微服务原型(node.js创建服务)
- 2025-07-28 JYM 设计模式系列- 责任链模式,装饰模式,让你的代码更优雅!
- 2025-07-28 一文搞懂JavaScript原型及原型链(附代码)
- 2025-07-28 JavaScript 原型链、prototype、__proto__详解
- 2025-07-28 Java设计模式面试题及答案整理(2025最新版)
- 2025-07-28 Java 面试题:Spring 配置 Bean 实例化有哪些方式?
你 发表评论:
欢迎- 最近发表
- 标签列表
-
- java反编译工具 (77)
- java反射 (57)
- java接口 (61)
- java随机数 (63)
- java7下载 (59)
- java数据结构 (61)
- java 三目运算符 (65)
- java对象转map (63)
- Java继承 (69)
- java字符串替换 (60)
- 快速排序java (59)
- java并发编程 (58)
- java api文档 (60)
- centos安装java (57)
- java调用webservice接口 (61)
- java深拷贝 (61)
- 工厂模式java (59)
- java代理模式 (59)
- java.lang (57)
- java连接mysql数据库 (67)
- java重载 (68)
- java 循环语句 (66)
- java反序列化 (58)
- java时间函数 (60)
- java是值传递还是引用传递 (62)
本文暂时没有评论,来添加一个吧(●'◡'●)