原型模式
1.1原型模式概述
1.1.1原型模式定义
原型模式(Prototype Pattern)指原型实例指定创建对象的种类,并且通过复制这些原型创建新的对象,属于创建型设计模式。
原型模式的核心在于复制原型对象。以系统中已存在的一个对象为原型,基于二进制流进行复制,不需要经历对象的初始化过程,提高了性能。
1.1.2原型模式的适用场景
- 适用于需要创建大对象,或者对象初始化非常耗时,占用太多资源,可以考虑使用原型模式
- 系统中大量使用该类对象,且调用者需要重新赋值的重复性操作过多。
1.1.4原型模式的通用写法
public interface IPrototype {
T clone();
}
public class ConcreatePrototypeA implements IPrototype{
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 Cloneable 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.
*
* Invoking Object's clone method on an instance that does not implement the
* Cloneable interface results in the exception
* CloneNotSupportedException being thrown.
*
* By convention, classes that implement this interface should override
* Object.clone (which is protected) with a public method.
* See {@link java.lang.Object#clone()} for details on overriding this
* method.
*
* Note that this interface does not contain the clone 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 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 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 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 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 ArrayList instance. (The
* elements themselves are not copied.)
*
* @return a clone of this ArrayList 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是对所有元素遍历一遍,这种也是实现深克隆的一种方式,但是这种相当于硬编码,更推荐序列化方式实现深克隆。
Original: https://www.cnblogs.com/amazing-yml/p/15961606.html
Author: amazing_yml
Title: 原型模式详解
原创文章受到原创版权保护。转载请注明出处:https://www.johngo689.com/644537/
转载文章受原作者版权保护。转载请注明原作者出处!