一、什么是原型模式?
原型模式 是创建型设计模式 ,核心思想:通过复制(拷贝)一个已存在的实例(原型),来创建新的对象 ,而不是通过 new 关键字重新创建。
核心特点
- 隐藏对象创建的复杂过程
- 性能比
new创建对象更高(尤其是大对象 / 复杂初始化) - 可以在运行时动态创建对象
- Java 实现核心:
Cloneable接口 +clone()方法
二、Java 实现方式
Java 提供了原生支持,分两种拷贝:
- 浅拷贝(默认):只复制基本类型,引用类型共用同一个对象
- 深拷贝:完全复制,包括引用类型的对象(相互独立)
1. 浅拷贝
步骤:
- 类实现
Cloneable接口(标记接口,无方法) - 重写
Object类的clone()方法
代码示例:
java
package prototype;
// 原型类:实现 Cloneable 接口
class User implements Cloneable {
private String name;
private int age;
public User(String name, int age) {
this.name = name;
this.age = age;
}
// 正常重写代码,但是返回的是Object,为了方便使用。我们自己重写下
// @Override
// protected Object clone() throws CloneNotSupportedException {
// return super.clone();
// }
@Override
protected User clone() {
try{
return (User) super.clone();
}catch (CloneNotSupportedException e){
throw new RuntimeException();
}
}
@Override
public String toString() {
return "name:"+name+"-age:"+age;
}
}
测试:
2、浅拷贝存在的问题:无法拷贝引用类型
举例
我们给User中再创建一个对象Role(省略get、set)
java
public class Role {
private String roleName;
public Role(String roleName) {
this.roleName = roleName;
}
}
每个用户一个角色(省略get、set)
java
// 原型类:实现 Cloneable 接口
class User implements Cloneable {
private String name;
private int age;
private Role role;
public User(String name, int age, Role role) {
this.name = name;
this.age = age;
this.role = role;
}
@Override
protected User clone() {
try{
return (User) super.clone();
}catch (CloneNotSupportedException e){
throw new RuntimeException();
}
}
@Override
public String toString() {
return "name:"+name+"-age:"+age;
}
}
测试结果
java
package prototype;
public class Test {
public static void main(String[] args) {
// 1. 创建原型对象
Role role = new Role("超级管理员");
User prototype = new User("张三", 20,role);
// 2. 拷贝新对象
User cloneUser = prototype.clone();
System.out.println(prototype); // name:张三-age:20-roleName:超级管理员
System.out.println(cloneUser); // name:张三-age:20-roleName:超级管理员
// !!!注意,这里我修改拷贝对象的角色
cloneUser.getRole().setRoleName("普通用户");
// 再来输出一次
System.out.println(prototype); // name:张三-age:20-roleName:普通用户
System.out.println(cloneUser); // name:张三-age:20-roleName:普通用户
}
}
这里我们可以看到我修改的是拷贝对象,但是原对象也被修改了
为什么出现这样的结果
因为在我们使用浅拷贝时,拷贝的对象的引用类型实际指向的还是原对对象的引用类型的地址,他们共用这个引用类型,所以修改一个会导致都被修改
3、 深拷贝(解决引用类型共用问题)
浅拷贝缺陷 :如果对象里有引用类型(如对象、集合),拷贝后会共用同一个引用,修改一个会影响另一个。
深拷贝目标:所有层级都完全复制,互不影响。
Java 深拷贝两种实现:
- 手动递归拷贝
- 序列化 / 反序列化
- 前拷贝后手动处理引用类型
示例(序列化深拷贝):
java
class Role2 implements Serializable {
private String roleName;
// 构造、get/set 省略
}
class User2 implements Serializable {
private String name;
private Role2 role; // 引用类型
// 深拷贝方法:序列化
public User2 deepClone() {
try {
// 写入流
ByteArrayOutputStream bos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(bos);
oos.writeObject(this);
// 读取流
ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray());
ObjectInputStream ois = new ObjectInputStream(bis);
return (User2) ois.readObject();
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}
测试:
java
public class Test {
public static void main(String[] args) {
User2 prototype = new User2("张三", new Role2("超级管理员"));
User2 cloneUser = prototype.deepClone();
// 修改拷贝对象的引用类型
cloneUser.getRole().setRoleName("普通用户");
// 原对象不受影响,深拷贝成功
System.out.println(prototype.getRole().getRoleName());
System.out.println(cloneUser.getRole().getRoleName());
}
}
三、浅拷贝 vs 深拷贝
| 特性 | 浅拷贝 | 深拷贝 |
|---|---|---|
| 实现难度 | 简单 | 稍复杂 |
| 引用类型 | 共用同一个对象 | 完全复制,独立对象 |
| 性能 | 高 | 较低 |
| 使用场景 | 无引用类型 / 允许共享 | 引用类型需要独立(绝大多数业务场景) |
四、优缺点
优点
- 性能极高 :比
new快,尤其复杂对象 - 简化创建:不用关心对象初始化细节
- 运行时创建:可动态拷贝不同状态的对象
- 不用依赖具体类,松耦合
缺点
- 每一个类都要考虑是否支持拷贝
- 深拷贝代码复杂
- 循环引用的对象拷贝会出问题
五、经典使用场景(Java 中)
1、ArrayList / HashMap 等集合的拷贝方法


2、Spring 中的 scope="prototype" bean
prototype 多例 Bean 本身是新建的,但它里面 @Autowired 注入的对象,默认全是单例,永远共用同一个引用
3、大量重复对象创建(如:报表、配置对象)
4、多线程环境下避免共享对象
总结
- 原型模式 = 拷贝对象创建新实例
- Java 实现:
Cloneable+clone() - 浅拷贝:快,但引用类型共享
- 深拷贝:完全独立,推荐序列化实现
- 适合:创建成本高、需要大量相似对象的场景
