一、描述:
将一个对象作为原型,通过对其进行复制而克隆出多个和原型类似的新实例。
二、特点:
用一个已经创建的实例作为原型,通过复制该原型对象来创建一个和原型相同或相似的新对象。原型实例指定了要创建的对象的种类。用这种方式创建对象非常高效,根本无须知道对象创建的细节。
三、优点:
(1)Java自带的原型模式基于内存二进制流的复制,在性能上比直接new一个对象更加优秀。
(2)可以使用深克隆方式保存对象的状态,使用原型模式将对象复制一份,并将其状态保存起来,简化了创建对象的过程,以便在需要的时候使用(例如恢复到历史某一状态),可辅助实现撤销操作。
四、应用:
Object的clone方法,实体类实现Cloneable接口类,重写clone()方法,clone方法有浅拷贝和深拷贝两种:
(1)浅拷贝:只拷贝当前主类的对象,建立一个新的复制对象,但是新的复制对象中的子对象依旧引用之前的主类的子对象地址。(注意:测试clone方法的结果,不能用@Data注解,因为@Data注解重写了hashCode方法,相同属性相同值的情况下,hashCode值一致。)
public class User implements Cloneable {
private String name;
private String sex;
private Integer age;
private School school;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getSex() {
return sex;
}
public void setSex(String sex) {
this.sex = sex;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
public School getSchool() {
return school;
}
public void setSchool(School school) {
this.school = school;
}
@Override
protected User clone() throws CloneNotSupportedException {
return (User) super.clone();
}
public static class School implements Cloneable {
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
}
}
测试:
@Slf4j
public class MainTest {
public static void main(String[] args) throws CloneNotSupportedException {
User user = new User();
user.setName("张三");
user.setAge(18);
User.School school = new User.School();
school.setName("华师小学");
user.setSchool(school);
User user2 = user.clone();
log.info("user:{}",JSON.toJSONString(user));
log.info("user2:{}",JSON.toJSONString(user2));
log.info("user-hashCode:{},user2-hashCode:{},isEquals:{}", user.hashCode(), user2.hashCode(), user.hashCode() == user2.hashCode());
log.info("user-school-hashCode:{},user2-school-hashCode:{}, isEquals:{}", user.getSchool().hashCode(), user2.getSchool().hashCode(), user.getSchool().hashCode() == user2.getSchool().hashCode());
}
}
运行结果:
user:{"age":18,"name":"张三","school":{"name":"华师小学"}}
user2:{"age":18,"name":"张三","school":{"name":"华师小学"}}
user-hashCode:662736689,user2-hashCode:1320677379,isEquals:false
user-school-hashCode:852687460,user2-school-hashCode:852687460, isEquals:true
从结果可以看出来,主对象在克隆之后,hashCode值变化,说明为克隆主对象又重新开辟了内存存储。但是克隆子对象school的hashCode值仍然为原来的hashCode,说明克隆后的user2对象中的school仍然引用的user的school对象地址。这就是浅拷贝。
浅拷贝的util方法有:
BeanUtil.copyProperties(user, user2);
(2)深拷贝:比浅拷贝更进一步,子对象也都重新开辟了新的内存存储,重写clone方法:
/**
* User
*
* @author zhouxy
* @date 2022/5/24
**/
public class User implements Cloneable {
private String name;
private String sex;
private Integer age;
private School school;
//getter、setter方法
......
@Override
protected User clone() throws CloneNotSupportedException {
User copyUser = (User) super.clone();
//重写school的clone方法
School school = copyUser.getSchool().clone();
copyUser.setSchool(school);
return copyUser;
}
public static class School implements Cloneable {
private String name;
//getter、setter方法
.....
@Override
protected School clone() throws CloneNotSupportedException {
return (School) super.clone();
}
}
}
测试代码仍然和浅拷贝的测试代码一致,直接显示测试结果:
user:{"age":18,"name":"张三","school":{"name":"华师小学"}}
user2:{"age":18,"name":"张三","school":{"name":"华师小学"}}
user-hashCode:662736689,user2-hashCode:1320677379,isEquals:false
user-school-hashCode:852687460,user2-school-hashCode:246399377, isEquals:false
school的对象的hashCode也发生了变化。