原型模式详解
原型模式
1.1原型模式概述
1.1.1原型模式定义
原型模式(Prototype Pattern)指原型实例指定创建对象的种类,并且通过复制这些原型创建新的对象,属于创建型设计模式。
原型模式的核心在于复制原型对象。以系统中已存在的一个对象为原型,基于二进制流进行复制,不需要经历对象的初始化过程,提高了性能。
1.1.2原型模式的适用场景
- 适用于需要创建大对象,或者对象初始化非常耗时,占用太多资源,可以考虑使用原型模式
- 系统中大量使用该类对象,且调用者需要重新赋值的重复性操作过多。
1.1.4原型模式的通用写法
public interface IPrototype<T> {
T clone();
}
public class ConcreatePrototypeA implements IPrototype<ConcreatePrototypeA>{
private String desc;
public ConcreatePrototypeA(String desc) {
this.desc = desc;
}
@Override
public ConcreatePrototypeA clone() {
return new ConcreatePrototypeA(this.desc);
}
@Override
public String toString() {
return "ConcreatePrototypeA{" +
"desc='" + desc + '\'' +
'}';
}
}
1.2深克隆与浅克隆
1.2.1浅克隆分析
在JAVA里,我们不需要创建原型接口,JAVA内置的原型接口---Cloneable接口,自定义的类只需要实现该接口并重写Object.clone()方法即可完成本类的复制。
这块需要注意,点开Cloneable接口查看源码:
/**
* A class implements the <code>Cloneable</code> interface to
* indicate to the {@link java.lang.Object#clone()} method that it
* is legal for that method to make a
* field-for-field copy of instances of that class.
* <p>
* Invoking Object's clone method on an instance that does not implement the
* <code>Cloneable</code> interface results in the exception
* <code>CloneNotSupportedException</code> being thrown.
* <p>
* By convention, classes that implement this interface should override
* <tt>Object.clone</tt> (which is protected) with a public method.
* See {@link java.lang.Object#clone()} for details on overriding this
* method.
* <p>
* Note that this interface does <i>not</i> contain the <tt>clone</tt> method.
* Therefore, it is not possible to clone an object merely by virtue of the
* fact that it implements this interface. Even if the clone method is invoked
* reflectively, there is no guarantee that it will succeed.
*
* @author unascribed
* @see java.lang.CloneNotSupportedException
* @see java.lang.Object#clone()
* @since JDK1.0
*/
public interface Cloneable {
}
可以看到Cloneable其实是空接口,而clone方法其实属于Object的方法。
翻译Cloneable接口的注释我们就明白了,Cloneable其实就是一个标记接口,只有实现这个接口后,然后在类中重写Object中的clone方法,然后通过类调用clone方法才能克隆成功,
如果不实现这个接口,则会抛出CloneNotSupportedException(克隆不被支持)异常。
接下来,做个小测试:
package org.example.prototype;
import lombok.Data;
import java.util.List;
@Data
public class Person implements Cloneable{
private int age;
private String name;
private List<String> hobbies;
public Person clone(){
try {
return (Person) super.clone();
} catch (CloneNotSupportedException e) {
e.printStackTrace();
return null;
}
}
@Override
public String toString() {
return "Person{" +
"age=" + age +
", name='" + name + '\'' +
", hobbies=" + hobbies +
'}';
}
}
package org.example.prototype;
import java.util.ArrayList;
import java.util.List;
public class PrototypeTest {
public static void main(String[] args) {
Person person = new Person();
person.setAge(18);
person.setName("yml");
List<String> hobbies = new ArrayList<>();
hobbies.add("篮球");
hobbies.add("唱跳rap");
person.setHobbies(hobbies);
System.out.println(person);
Person clone = person.clone();
clone.getHobbies().add("游泳");
System.out.println(person);
}
}
上边代码使用super.clone给Person对象进行克隆,当我们改变克隆对象的属性时,得到的结果:
以上结果说明,原型对象和克隆对象的引用对象属性指向同一对象的引用,意味着这种方式克隆的不是值而是引用的地址。这个显然不符合我们的预期,因为我们希望克隆对象和原型对象是两个独立的对象,互不影响。
Java自带的克隆方式为浅克隆。而如果我们想进行深克隆,可以浅克隆之后,手动给克隆对象的相关属性分配内存,不过这种方式太重复且繁琐,在Java中,一般使用序列化和反序列化实现深克隆。
1.2.2使用序列化实现深克隆
package org.example.prototype;
import lombok.Data;
import java.io.*;
import java.util.List;
@Data
public class Person implements Cloneable,Serializable{
private int age;
private String name;
private List<String> hobbies;
public Person clone(){
try {
return (Person) super.clone();
} catch (CloneNotSupportedException e) {
e.printStackTrace();
return null;
}
}
public Person deepClone() throws IOException, ClassNotFoundException {
ByteArrayOutputStream bos = new ByteArrayOutputStream();
ObjectOutputStream stream = new ObjectOutputStream(bos);
stream.writeObject(this);
ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(bos.toByteArray());
ObjectInputStream objectInputStream = new ObjectInputStream(byteArrayInputStream);
return (Person) objectInputStream.readObject();
}
@Override
public String toString() {
return "Person{" +
"age=" + age +
", name='" + name + '\'' +
", hobbies=" + hobbies +
'}';
}
}
public class PrototypeTest {
public static void main(String[] args) throws IOException, ClassNotFoundException {
Person person = new Person();
person.setAge(18);
person.setName("yml");
List<String> hobbies = new ArrayList<>();
hobbies.add("篮球");
hobbies.add("唱跳rap");
person.setHobbies(hobbies);
System.out.println(person);
Person deepClone = person.deepClone();
deepClone.getHobbies().add("游泳");
System.out.println(person);
}
}
从运行结果看来,我们的确实现了深克隆。
1.2.3原型模式在源码中的应用
- JDK中ArrayList源码
/**
* Returns a shallow copy of this <tt>ArrayList</tt> instance. (The
* elements themselves are not copied.)
*
* @return a clone of this <tt>ArrayList</tt> instance
*/
public Object clone() {
try {
ArrayList<?> v = (ArrayList<?>) super.clone();
v.elementData = Arrays.copyOf(elementData, size);
v.modCount = 0;
return v;
} catch (CloneNotSupportedException e) {
// this shouldn't happen, since we are Cloneable
throw new InternalError(e);
}
}
其实ArrayList是对所有元素遍历一遍,这种也是实现深克隆的一种方式,但是这种相当于硬编码,更推荐序列化方式实现深克隆。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· DeepSeek 开源周回顾「GitHub 热点速览」
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· AI与.NET技术实操系列(二):开始使用ML.NET
· 单线程的Redis速度为什么快?