原型模式,用原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象。它其实就是从一个对象再创建另一个可定制的对象,而且不需要知道任何创建的细节。
1、现实Cloneable接口的抽象类Prototype
public abstract class Prototype implements Cloneable
{
public String id;
public Prototype(String id)
{
this.id=id;
}
public String getId()
{
return id;
}
public abstract Prototype Clone() throws Exception;//关键的clone方法
}
2、继承prototype的具体类Concreteprorotype
public class ConcretePrototype extends Prototype
{
public ConcretePrototype(String id)
{
super(id);
}
public Prototype Clone() throws Exception
{
return (Prototype)this.clone();
}
}
3、客户端类
public class Client
{
public static void main(String[] args) throws Exception
{
ConcretePrototype p1=new ConcretePrototype("1");
ConcretePrototype c1=(ConcretePrototype)p1.Clone();
System.out.println("c1:"+c1.id);
}
}
实例应用,简历复制
1、实现Cloneable的简历类
public class Resume implements Cloneable
{
private String name;
private String sex;
private String age;
private WorkExperience work;
public Resume(String name)
{
this.name=name;
work=new WorkExperience();
}
public void setPersonalInfo(String sex,String age)
{
this.sex=sex;
this.age=age;
}
public void setWorkExperience(String workDate,String workCompany)
{
work.WorkDate=workDate;
work.WorkCompany=workCompany;
}
public void display()
{
System.out.println("name:"+name+":sex:"+sex+":age:"+age);
System.out.println("workDate:"+work.WorkDate+":workCompany:"+work.WorkCompany);
}
public Object Clone() throws Exception
{
return this.clone();
}
}
2、WorkExperience类实现
public class WorkExperience
{
private String workDate;
public String WorkDate;
private String workCompany;
public String WorkCompany;
public String getWorkDate()
{
return workDate;
}
public void setWorkDate(String workDate)
{
this.workDate = workDate;
}
public String getWorkCompany()
{
return workCompany;
}
public void setWorkCompany(String workCompany)
{
this.workCompany = workCompany;
}
}
3、客户端类
public class Client
{
public static void main(String[] args) throws Exception
{
Resume a=new Resume("A");
a.setPersonalInfo("female","20");
a.setWorkExperience("2010-2011","xx公司");
Resume b=(Resume)a.Clone();
b.setWorkExperience("2010-2011","yy公司");
a.display();
b.display();
}
}
预测结果:
name:A:sex:female:age:20
workDate:2010-2011:workCompany:xx公司
name:A:sex:female:age:20
workDate:2010-2011:workCompany:yy公司
实际结果:
原因:涉及到浅复制与深复制的概念
clone()的方法,如果字段是值类型的,则对该字段进行逐位复制。如果字段是引用类型的,则复制引用不复制引用的对象,也就是说复制后的引用和之前的引用是指向同一个地方的。在这里面因为WorkExperience work是对象引用,那么它的数据不会被复制过来,只会复制引用。这就是浅复制,被复制对象的所有变量都含有与原来的对象相同的值,而所有的对其他对象的引用都仍然指向原来的对象。深复制就是把引用对象的变量指向复制过后的新变量,而不是原有的被引用的对象
代码修改:
1、WorkExperience代码修改
public class WorkExperience <span style="color:#FF6666;">implements Cloneable</span>//实现Cloneable接口
{
private String workDate;
public String WorkDate;
private String workCompany;
public String WorkCompany;
public String getWorkDate()
{
return workDate;
}
public void setWorkDate(String workDate)
{
this.workDate = workDate;
}
public String getWorkCompany()
{
return workCompany;
}
public void setWorkCompany(String workCompany)
{
this.workCompany = workCompany;
}
<span style="color:#FF6666;">public Object Clone() throws Exception<span style="color:#000000;">//复制方法</span>
{
return this.clone();
}</span>
}
2、Resume代码修改
public class Resume implements Cloneable
{
private String name;
private String sex;
private String age;
<span style="color:#FF6666;">private WorkExperience work;</span>
public Resume(String name)
{
this.name=name;
work=new WorkExperience();
}
@SuppressWarnings("unused")
<span style="color:#FF6666;">private Resume(WorkExperience work) throws Exception
{
this.work=(WorkExperience)work.Clone();
}</span>
public void setPersonalInfo(String sex,String age)
{
this.sex=sex;
this.age=age;
}
public void setWorkExperience(String workDate,String workCompany)
{
work.WorkDate=workDate;
work.WorkCompany=workCompany;
}
public void display()
{
System.out.println("name:"+name+":sex:"+sex+":age:"+age);
System.out.println("workDate:"+work.WorkDate+":workCompany:"+work.WorkCompany);
}
<span style="color:#FF6666;">public Object Clone() throws Exception
{
Resume resume=new Resume(this.work);//调用私有的构造方法,让工作经历克隆完成,然后再给这个简历对象的相关字段复制,最后返回一个深复制的简历对象
resume.sex=this.sex;
resume.name=this.name;
resume.age=this.age;
return resume;
}</span>
}
运行结果如下: