Java设计模式之《原型模式》--深、浅copy
目录
1、原型模式
1.1、定义
1.2、使用场景
1.3、与bean的区别
2、浅拷贝 vs 深拷贝
2.1、分类
1、浅拷贝
2、深拷贝
2.2、实现方式
1、手动 clone 引用成员
2、序列化做深拷贝
3、原型注册表
前言
原型模式属于创建型模式。用于创建重复的对象,同时又能保证性能。要求原型对象实现一个“克隆”自身的方法,通过调用这个方法来创建一个新的实例对象。
1、原型模式
1.1、定义
通过复制(克隆)已有对象来创建新对象,而不是通过 new 构造函数来创建。原型对象保存了实例的初始状态,客户端通过克隆原型得到新的对象。
如下图所示:
1.2、使用场景
什么时候用原型模式更合适?
1、对象创建成本高(比如大量属性赋值、复杂初始化);
2、需要运行时决定要创建哪种具体类实例;
3、需要大量相似对象(使用原型复制比重复 new 和初始化更高效)。
1.3、与bean的区别
1、原型模式(Prototype Pattern):
一种“创建型设计模式”,通过复制(克隆)已有对象来创建新对象,关注如何复制对象(clone / 拷贝构造 / 序列化等)。
通常靠对象自身实现复制逻辑(Object.clone、copy constructor、序列化等)。
2、Spring 的 prototype scope:
Spring 容器中一个“作用域/生命周期策略”,表示每次从容器获取该 bean 都会创建一个新的实例,关注的是容器如何管理 bean 的生命周期。
由容器负责新建(调用构造器/工厂方法并做依赖注入),并不要求对象实现 clone。
具体可参考:Spring的Bean原型模式下的使用_spring 原型bean-CSDN博客文章浏览阅读1.2k次,点赞33次,收藏30次。摘要:本文探讨了Spring中原型(Prototype)模式Bean的使用问题与解决方案。问题原因包括@Autowired注入只初始化一次、代理模式问题及不当获取方式。提供了四种解决方案:通过ApplicationContext获取、使用ObjectProvider、Lookup方法和Provider接口。分析了原型模式的适用场景:有状态Bean、线程不安全对象、需要新实例及避免副作用的场景。最后指出使用注意事项,包括内存管理、性能影响、依赖管理和测试复杂性,强调需权衡资源开销与功能需求。_spring 原型beanhttps://dyclt.blog.csdn.net/article/details/149182196?spm=1011.2415.3001.5331
2、浅拷贝 vs 深拷贝
2.1、分类
1、浅拷贝
复制对象本身,但对象内部的引用类型字段(比如其他对象、集合)只是复制引用,仍然指向同一个子对象。
换句话说,“复制的是外壳,里面的东西是共享的”。
⚠️注意:
如果原型对象的成员变量是值类型(byte,short,int,long,char,double,float,boolean).那么就直接复制;如果是复杂的类型,(如枚举、对象)就只复制对应的内存地址。
2、深拷贝
不仅复制对象本身,也递归复制它引用的所有可变子对象,得到真正独立的完整副本。
换句话说,“里面的东西也全部复制了一份”。
2.2、实现方式
实现方式概述
- 使用 Cloneable + Object.clone()(容易实现浅拷贝,做深拷贝需手动 clone 引用字段)
- 使用拷贝构造函数(推荐:明确、可控)
- 使用序列化(Serializable)深拷贝(简便但开销高)
- 使用原型注册表(Registry)统一管理可克隆原型
1:浅拷贝(使用 Cloneable、super.clone())
// Address.java
class Address implements Cloneable {String city;Address(String city) { this.city = city; }@Overridepublic Address clone() {try {return (Address) super.clone();} catch (CloneNotSupportedException e) {throw new AssertionError();}}@Overridepublic String toString() { return "Address{city='" + city + "'}"; }
}// PersonShallow.java
class PersonShallow implements Cloneable {String name;int age;Address address; // 引用类型PersonShallow(String name, int age, Address address) {this.name = name; this.age = age; this.address = address;}@Overridepublic PersonShallow clone() {try {// super.clone() 做的是浅拷贝:基本类型复制,引用复制引用return (PersonShallow) super.clone();} catch (CloneNotSupportedException e) {throw new AssertionError();}}@Overridepublic String toString() {return "PersonShallow{name='" + name + "', age=" + age + ", address=" + address + "}";}
}// MainShallowDemo.java
public class MainShallowDemo {public static void main(String[] args) {Address addr = new Address("Beijing");PersonShallow p1 = new PersonShallow("Alice", 30, addr);PersonShallow p2 = p1.clone();System.out.println("Before change:");System.out.println("p1 = " + p1);System.out.println("p2 = " + p2);// 修改 p2 的 address,会影响 p1(因为浅拷贝共享同一个 Address)p2.address.city = "Shanghai";System.out.println("After change:");System.out.println("p1 = " + p1);System.out.println("p2 = " + p2);}
}
修改克隆对象中引用类型会影响原对象。
2:深拷贝(clone 内部手动 clone 引用成员或使用序列化)
1、手动 clone 引用成员
(建议)
// PersonDeep.java
class PersonDeep implements Cloneable {String name;int age;Address address;PersonDeep(String name, int age, Address address) {this.name = name; this.age = age; this.address = address;}@Overridepublic PersonDeep clone() {try {PersonDeep copy = (PersonDeep) super.clone();// 手动深拷贝引用对象copy.address = this.address.clone();return copy;} catch (CloneNotSupportedException e) {throw new AssertionError();}}@Overridepublic String toString() {return "PersonDeep{name='" + name + "', age=" + age + ", address=" + address + "}";}
}// MainDeepDemo.java
public class MainDeepDemo {public static void main(String[] args) {Address addr = new Address("Beijing");PersonDeep p1 = new PersonDeep("Bob", 40, addr);PersonDeep p2 = p1.clone();System.out.println("Before change:");System.out.println("p1 = " + p1);System.out.println("p2 = " + p2);p2.address.city = "Guangzhou"; // 不会影响 p1System.out.println("After change:");System.out.println("p1 = " + p1);System.out.println("p2 = " + p2);}
}
2、序列化做深拷贝
(可用于任意深度,但性能/可读性较差)
import java.io.*;// 使用可序列化的类
class AddressS implements Serializable {String city;AddressS(String city) { this.city = city; }public String toString() { return "AddressS{city='" + city + "'}"; }
}class PersonS implements Serializable {String name;int age;AddressS address;PersonS(String name, int age, AddressS address) {this.name = name; this.age = age; this.address = address;}public String toString() { return "PersonS{name='" + name + "', age=" + age + ", address=" + address + "}"; }
}public class SerializeDeepCopy {@SuppressWarnings("unchecked")public static <T extends Serializable> T deepCopy(T obj) {try {ByteArrayOutputStream bos = new ByteArrayOutputStream();ObjectOutputStream out = new ObjectOutputStream(bos);out.writeObject(obj);out.flush();ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray());ObjectInputStream in = new ObjectInputStream(bis);return (T) in.readObject();} catch (IOException | ClassNotFoundException e) {throw new RuntimeException(e);}}public static void main(String[] args) {PersonS p1 = new PersonS("Cathy", 28, new AddressS("Shenzhen"));PersonS p2 = deepCopy(p1);p2.address.city = "Hangzhou";System.out.println("p1 = " + p1);System.out.println("p2 = " + p2);}
}
3、原型注册表
import java.util.*;// 简单的 Prototype 接口(返回 Object 或 Prototype 的 clone)
interface Prototype {Prototype clone();
}// 一个具体原型
class ConcretePrototype implements Prototype {private String id;private Map<String, String> data = new HashMap<>();ConcretePrototype(String id) { this.id = id; }void put(String k, String v) { data.put(k, v); }String get(String k) { return data.get(k); }@Overridepublic Prototype clone() {// 简单示例做浅拷贝 + 拷贝集合内容以避免共享ConcretePrototype copy = new ConcretePrototype(this.id);copy.data = new HashMap<>(this.data);return copy;}@Overridepublic String toString() {return "ConcretePrototype{id='" + id + "', data=" + data + "}";}
}class PrototypeRegistry {private Map<String, Prototype> registry = new HashMap<>();void register(String key, Prototype prototype) { registry.put(key, prototype); }Prototype create(String key) {Prototype p = registry.get(key);return (p != null) ? p.clone() : null;}
}// Usage
public class PrototypeRegistryDemo {public static void main(String[] args) {PrototypeRegistry reg = new PrototypeRegistry();ConcretePrototype protoA = new ConcretePrototype("A");protoA.put("name", "TemplateA");reg.register("A", protoA);ConcretePrototype instance = (ConcretePrototype) reg.create("A");instance.put("name", "InstanceFromA");System.out.println("protoA = " + protoA);System.out.println("instance = " + instance);}
}
总结
原型模式通过对象的复制机制,提供了一种高效创建相似对象的方式。浅克隆适用于简单对象,深克隆则适合复杂对象结构。
在 Java 中,借助 Cloneable 接口和 Clone()
方法,可以轻松实现原型模式。
参考文章:
1、Java 设计模式:原型模式详解_java 原型模式-CSDN博客文章浏览阅读533次,点赞5次,收藏10次。原型模式的核心思想是:通过克隆已有对象(原型)来生成新对象,而不是通过构造函数重新创建。它利用对象的复制机制,提高创建效率,并支持动态扩展。_java 原型模式https://blog.csdn.net/NepalTrip/article/details/147085502?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522ae2f8b02105801ca1c69be6444a5bb9c%2522%252C%2522scm%2522%253A%252220140713.130102334..%2522%257D&request_id=ae2f8b02105801ca1c69be6444a5bb9c&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2~blog~sobaiduend~default-2-147085502-null-null.nonecase&utm_term=java%E8%AE%BE%E8%AE%A1%E6%A8%A1%E5%BC%8F%E7%9A%84%E5%8E%9F%E5%9E%8B%E6%A8%A1%E5%BC%8F&spm=1018.2226.3001.4450
2、设计模式精讲-CSDN博客文章浏览阅读10w+次,点赞607次,收藏4.9k次。设计模式是一套经过反复使用的代码设计经验,目的是为了重用代码、让代码更容易被他人理解、保证代码可靠性。设计模式于己于人于系统都是多赢的,它使得代码编写真正工程化,它是软件工程的基石,如同大厦的一块块砖石一样。项目中合理的运用设计模式可以完美的解决很多问题,每种模式在现实中都有相应的原理来与之对应,每种模式描述了一个在我们周围不断重复发生的问题,以及该问题的核心解决方案,这也是它能被广泛应用的原因。总体来说,设计模式分为三大类:5种创建型模式、7种结构型模式、11种行为型模式_java 设计模式https://blog.csdn.net/a745233700/article/details/120371090?ops_request_misc=%257B%2522request%255Fid%2522%253A%252240413392cc5f347aebaa2a1bfe2e970e%2522%252C%2522scm%2522%253A%252220140713.130102334.pc%255Fblog.%2522%257D&request_id=40413392cc5f347aebaa2a1bfe2e970e&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2~blog~first_rank_ecpm_v1~rank_v31_ecpm-6-120371090-null-null.nonecase&utm_term=java%E8%AE%BE%E8%AE%A1%E6%A8%A1%E5%BC%8F%E7%9A%84%E5%8E%9F%E5%9E%8B%E6%A8%A1%E5%BC%8F&spm=1018.2226.3001.4450