简历复印——原型模式
小菜的简历,大鸟给小菜提出了一个要求:有一个简历类,必须有姓名,可以设置性别,年龄和工作经历,最终大鸟要三份简历。
简历代码的初步实现
- 简历类
package prototype01;
public class Resume {
private String name;
private String sex;
private String age;
private String timeArea;
private String company;
public Resume(String name){
this.name = name;
}
// 设置个人信息
public void setPersonInfo(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(name + " " + sex + " " + age);
System.out.println("工作经历:" + timeArea + " " + company);
}
}
- 客户端
package prototype01;
public class Client {
public static void main(String[] args) {
Resume resumeA = new Resume("小菜");
resumeA.setPersonInfo("男","22");
resumeA.setWorkExperience("2018 - 2019","XX公司");
resumeA.display();
Resume resumeB = new Resume("小菜");
resumeB.setPersonInfo("男","22");
resumeB.setWorkExperience("2018 - 2019","XX公司");
resumeB.display();
Resume resumeC = new Resume("小菜");
resumeC.setPersonInfo("男","22");
resumeC.setWorkExperience("2018 - 2019","XX公司");
resumeC.display();
}
}
/* out:
小菜 男 22
工作经历:2018 - 2019 XX公司
小菜 男 22
工作经历:2018 - 2019 XX公司
小菜 男 22
工作经历:2018 - 2019 XX公司
*/
三分简历需要实例化三次,这样的客户端代码很麻烦,如果我要200份简历,你就得实例化200次。
并且如果我设置参数错误的话就要修改好多次。
- 想一想,为什么我们不这么写?
public static void main(String[] args) {
Resume resumeA = new Resume("小菜");
resumeA.setPersonInfo("男","22");
resumeA.setWorkExperience("2018 - 2019","XX公司");
Resume resumeB = resumeA;
Resume resumeC = resumeA;
resumeA.display();
resumeB.display();
resumeC.display();
}
这其实是传引用,也就是resumeA,resumeB,resumeC存的地址是相同的,指向同一对象,并没有复制三份。
- 那有什么办法解决呢?
- 其实Java给我们提供了Clone这样的方法。
原型模式
原型模式(Prototype),用原型实例指定创建对象的类型
,并通过拷贝这些原型来创建新的对象。
原型模式就是从一个对象创建出另外一个可定制的对象,而且不需要知道任何细节。
- 原型类
package prototype02;
public abstract class Prototype implements Cloneable{
private String id;
public Prototype(String id){
this.id = id;
}
public String getId() {
return id;
}
public abstract Prototype myClone() throws CloneNotSupportedException;
}
- 具体原型类
package prototype02;
import java.util.Objects;
public class ConcretePrototype1 extends Prototype {
public ConcretePrototype1(String id) {
super(id);
}
/**
* 浅克隆
* clone出来的对象与原对象完全一致,
* 引用变量还是原来对用的引用【拷贝对象时仅仅拷贝对象本身(包括对象中的基本变量),而不拷贝对象包含的引用指向的对象。】
* 即若对象中有引用数据类型的变量则无法拷贝
* */
@Override
public Prototype myClone() throws CloneNotSupportedException {
return (Prototype)this.clone();
}
// 子类重写Object中的clone
// 本质上还是Object中的clone
@Override
public Object clone() throws CloneNotSupportedException {
return super.clone();
}
/*
* 在Java中,实现浅克隆通常意味着你需要重写对象的clone()方法。
* Java中的Object类提供了一个默认的clone()方法,但这个默认实现是受保护的,
* 因此你需要让你的类实现Cloneable接口(尽管这个接口是一个标记接口,没有任何方法),
* 并且重写clone()方法以使其为public。
* */
}
- 客户端
package prototype02;
public class Client {
public static void main(String[] args) throws CloneNotSupportedException {
ConcretePrototype1 p1 = new ConcretePrototype1("0721");
ConcretePrototype1 c1 = (ConcretePrototype1)p1.myClone();
System.out.println("clone:" + c1.getId());
System.out.println(p1);
System.out.println(c1);
}
}
/* out:
clone:0721
prototype02.ConcretePrototype1@7291c18f
prototype02.ConcretePrototype1@34a245ab
*/
简历的原型实现
- 简历类
package prototype03;
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 setPersonInfo(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(name + " " + sex + " " + age);
System.out.println("工作经历:" + timeArea + " " + company);
}
// 浅克隆,实际上是Obeject.clone();
@Override
public Object clone() throws CloneNotSupportedException {
return super.clone();
}
}
- 客户端
package prototype03;
public class Client {
public static void main(String[] args) throws CloneNotSupportedException {
Resume resumeA = new Resume("小菜");
resumeA.setPersonInfo("男","22");
resumeA.setWorkExperience("2018 - 2019","XX公司");
// 浅克隆,不能克隆引用对象。
Resume c1 = (Resume)resumeA.clone();
c1.setWorkExperience("1999 - 2000","YY公司");
Resume c2 = (Resume)resumeA.clone();
c2.setWorkExperience("2012 - 2013","ZZ公司");
resumeA.display();
c1.display();
c2.display();
System.out.println(resumeA);
System.out.println(c1);
System.out.println(c2);
}
}
/* out:
小菜 男 22
工作经历:2018 - 2019 XX公司
小菜 男 22
工作经历:1999 - 2000 YY公司
小菜 男 22
工作经历:2012 - 2013 ZZ公司
prototype03.Resume@cc34f4d
prototype03.Resume@17a7cec2
prototype03.Resume@65b3120a
*/
-
每new一次,都要执行一次构造函数,如果构造函数的执行时间很长,那么多次构造就显得很低效了。
-
一般在初始化的信息不变的前提下,克隆是最好的方法。
-
隐藏了对象创建的内部细节,也提高了性能。
-
不用重新初始化对象,而是动态地获取对象运行时的状态。
浅克隆与深克隆
Object.clone():浅拷贝是按位拷贝对象,它会创建一个新对象,这个对象有着原始对象属性值的一份精确拷贝。
如果属性是基本类型,拷贝的就是基本类型的值;
如果属性是内存地址(引用类型),拷贝的就是内存地址 ,因此如果其中一个对象改变了这个地址,就会影响到另一个对象。
即默认拷贝构造函数只是对对象进行浅拷贝复制(逐个成员依次拷贝),即只复制对象空间而不复制资源。
- 工作经历类
package prototype04;
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;
}
}
- 简历类
package prototype04;
public class Resume implements Cloneable{
private String name;
private String sex;
private String age;
// 引用对象
private WorkExperience workExperience;
public Resume(String name){
this.name = name;
this.workExperience = new WorkExperience();
}
// 设置个人信息
public void setPersonInfo(String sex,String age){
this.sex = sex;
this.age = age;
}
// 设置工作经历
public void setWorkExperience(String timeArea,String company){
workExperience.setTimeArea(timeArea);
workExperience.setCompany(company);
}
public void display(){
System.out.println(name + " " + sex + " " + age);
System.out.println("工作经历:" + workExperience.getTimeArea() + " " + workExperience.getCompany());
}
@Override
public Object clone() throws CloneNotSupportedException {
return super.clone();
}
}
- 客户端
package prototype04;
public class Client {
public static void main(String[] args) throws CloneNotSupportedException {
Resume resumeA = new Resume("小菜");
resumeA.setPersonInfo("男","22");
resumeA.setWorkExperience("2018 - 2019","XX公司");
// 浅克隆,不能克隆引用对象。
Resume c1 = (Resume)resumeA.clone();
c1.setWorkExperience("1999 - 2000","YY公司");
Resume c2 = (Resume)resumeA.clone();
c2.setWorkExperience("2012 - 2013","ZZ公司");
resumeA.display();
c1.display();
c2.display();
System.out.println(resumeA);
System.out.println(c1);
System.out.println(c2);
}
}
/* out:
小菜 男 22
工作经历:2012 - 2013 ZZ公司
小菜 男 22
工作经历:2012 - 2013 ZZ公司
小菜 男 22
工作经历:2012 - 2013 ZZ公司
prototype04.Resume@17a7cec2
prototype04.Resume@65b3120a
prototype04.Resume@6f539caf
*/
-
浅克隆:
-
clone出来的对象与原对象完全一致。
-
引用变量还是原来对象的引用【拷贝对象时仅仅拷贝对象本身(包括对象中的基本变量),而不拷贝对象包含的引用指向的对象。】
-
即若对象中有引用数据类型的变量则无法拷贝。
-
深克隆:
- 会复制引用的对象,而不是指向原来的对象。
简历深复制的实现
- 工作经历类
package prototype05;
// 实现Cloneable接口,表示有克隆的能力。
public class WorkExperience implements Cloneable{
private String timeArea;
private String company;
@Override
public Object clone() throws CloneNotSupportedException {
return super.clone();
}
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;
}
}
- 简历类
package prototype05;
public class Resume implements Cloneable{
private String name;
private String sex;
private String age;
// 引用对象
private WorkExperience workExperience;
public Resume(String name){
this.name = name;
this.workExperience = new WorkExperience();
}
// 克隆引用对象
private Resume(WorkExperience workExperience) throws CloneNotSupportedException {
this.workExperience = (WorkExperience)workExperience.clone();
}
// 设置个人信息
public void setPersonInfo(String sex,String age){
this.sex = sex;
this.age = age;
}
// 设置工作经历
public void setWorkExperience(String timeArea,String company){
workExperience.setTimeArea(timeArea);
workExperience.setCompany(company);
}
public void display(){
System.out.println(name + " " + sex + " " + age);
System.out.println("工作经历:" + workExperience.getTimeArea() + " " + workExperience.getCompany());
}
// 深克隆(需要手动克隆)
@Override
public Object clone() throws CloneNotSupportedException {
Resume resume = new Resume(this.workExperience);
resume.setPersonInfo(this.sex,this.age);
resume.setName(this.name);
return resume;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
/*
小菜 男 22
工作经历:2018 - 2019 XX公司
小菜 男 22
工作经历:1999 - 2000 YY公司
小菜 男 22
工作经历:2012 - 2013 ZZ公司
prototype05.Resume@17a7cec2
prototype05.Resume@65b3120a
prototype05.Resume@6f539caf
*/