定义
从一个对象再创建另外一个可定制的对象,而且不需要知道任何创建的细节
场景举例
/**
* 有这么一个场景 使用原型模式复印多份的简历
* 1. 首先需要创建一个简历类 包含名字 性别 年龄 时间 公司
* 2. 通过实现clone接口复制多份简历
*/
第一步: 创建该简历类并实现clone接口
public class Resume implements Cloneable{
private String name;
private String sex;
private String age;
private String timeArea;
private String company;
public Resume(String name) {
this.name = name;
}
public void setPersonalInfo(String sex, String age){
this.sex = sex;
this.age = age;
}
public void setWorkExperience(String timeArea, String company) {
this.timeArea = timeArea;
this.company = company;
}
// 展示简历
public void display(){
System.out.println(this.name + " " + this.sex + " " + this.age);
System.out.println("工作经历" + this.timeArea + " " + this.company);
}
@Override
public Resume clone() {
Resume clone = null;
try {
clone = (Resume) super.clone();
return clone;
} catch (CloneNotSupportedException e) {
System.out.println("Clone异常");
}
return clone;
}
}
第二步: 客户端实现
public static void main(String[] args) {
Resume resume = new Resume("张三");
resume.setPersonalInfo("男","26");
resume.setWorkExperience("2022-07-18","移动");
Resume resume1 = resume.clone();
resume.setPersonalInfo("男","27");
resume.setWorkExperience("2021-07-18","联通");
Resume resume2 = resume.clone();
resume.setPersonalInfo("男","25");
resume.setWorkExperience("2023-07-18","电信");
resume.display();
resume1.display();
resume2.display();
}
分析: 简历实体类通过实现cloneable接口,重写clone方法,可以实现只new一个对象就可以创建出另外一个定制的对象。但是,我们此时看到的简历类中都是些基础的属性,如果简历类中也有一个对象呢,也能顺利的创建一个可定制的对象吗?
浅拷贝/浅复制
需求升级:假设我们的简历上面的工作时间和工作地点用一个工作经历类来表示
工作经历类
public class WorkExperience {
private String timeArea;
private String company;
public String getTimeArea() {
return timeArea;
}
public void setTimeArea(String timeArea) {
this.timeArea = timeArea;
}
public String getCompany() {
return company;
}
public void setCompany(String company) {
this.company = company;
}
}
简历类中的实现
public class Resume implements Cloneable{
private String name;
private String sex;
private String age;
// private String timeArea;
//
// private String company;
private WorkExperience workExperience;
public Resume(String name) {
this.name = name;
this.workExperience = new WorkExperience();
}
public void setPersonalInfo(String sex, String age){
this.sex = sex;
this.age = age;
}
public void setWorkExperience(String timeArea, String company) {
this.workExperience.setTimeArea(timeArea);
this.workExperience.setCompany(company);
}
// 展示简历
public void display(){
System.out.println(this.name + " " + this.sex + " " + this.age);
System.out.println("工作经历" + this.workExperience.getTimeArea() + " " + this.workExperience.getCompany());
}
@Override
public Resume clone() {
Resume clone = null;
try {
clone = (Resume) super.clone();
return clone;
} catch (CloneNotSupportedException e) {
System.out.println("Clone异常");
}
return clone;
}
}
输出结果:
分析: 通过输出结果可以看到, 每一份简历的工作经历输出的都是最后的一次赋值,也就是说,对引用类型,只是复制了引用,指向的还是原来的对象,也就是说 每一次的拷贝都是指向的同一个对象,因此只会保留最后一次的对象赋值。那么怎么把引用对象的变量指向复制过的新对象呢,而不是原有的被引用的对象? 深拷贝/深复制
深拷贝/深复制
在工作经历类中同样也继承 cloneable接口 实现clone方法
public class WorkExperience implements Cloneable{
private String timeArea;
private String company;
public String getTimeArea() {
return timeArea;
}
public void setTimeArea(String timeArea) {
this.timeArea = timeArea;
}
public String getCompany() {
return company;
}
public void setCompany(String company) {
this.company = company;
}
@Override
public WorkExperience clone() {
WorkExperience workExperience = null;
try {
workExperience = (WorkExperience) super.clone();
return workExperience;
} catch (CloneNotSupportedException e) {
System.err.println("Clone异常");
}
return workExperience;
}
}
在简历类中同样再次的拷贝一次工作经历类的clone方法
public class Resume implements Cloneable{
private String name;
private String sex;
private String age;
// private String timeArea;
//
// private String company;
private WorkExperience workExperience;
public Resume(String name) {
this.name = name;
this.workExperience = new WorkExperience();
}
public void setPersonalInfo(String sex, String age){
this.sex = sex;
this.age = age;
}
public void setWorkExperience(String timeArea, String company) {
this.workExperience.setTimeArea(timeArea);
this.workExperience.setCompany(company);
}
// 展示简历
public void display(){
System.out.println(this.name + " " + this.sex + " " + this.age);
System.out.println("工作经历" + this.workExperience.getTimeArea() + " " + this.workExperience.getCompany());
}
@Override
public Resume clone() {
Resume resume = null;
try {
resume = (Resume) super.clone();
resume.workExperience = this.workExperience.clone();
return resume;
} catch (CloneNotSupportedException e) {
System.err.println("Clone异常");
}
return resume;
}
}
分析 : 可以看到 使用了一个引用对象,则会进行一次深拷贝。那么如果引用对象有好几层,就需要深拷贝多少层吗?这个确实是个问题,深复制要复制到多少层,需要事先就考虑好,并且还要当心出现循环引用的问题,只能根据实际问题实际分析了。
优缺点
优点(使用场景): 1.类初始化消耗资源较多 2. new产生的一个对象需要非常繁琐的过程(数据准备、访问权限等) 3.构造函数比较复杂 4.循环体中生产大量对象时,可读性下降
缺点: 1.必须具备克隆(或者可拷贝)方法 2.对克隆复杂对象或对克隆出的对象进行复杂改造时,易带来风险 3.深拷贝、浅拷贝要运用得当