引言:
创建对象的五种方式:
Clone方法:
其实现方式正是通过调用 Object 类的 clone() 方法来完成。
protected native Object clone() throws CloneNotSupportedException;
public class Person implements Cloneable{
public String pname;
public int page;
public Address address;
public Person() {}
public Person(String pname,int page){
this.pname = pname;
this.page = page;
this.address = new Address();
}
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
public void setAddress(String provices,String city ){
address.setAddress(provices, city);
}
public void display(String name){
System.out.println(name+":"+"pname=" + pname + ", page=" + page +","+ address);
}
public String getPname() {
return pname;
}
public void setPname(String pname) {
this.pname = pname;
}
public int getPage() {
return page;
}
public void setPage(int page) {
this.page = page;
}
}
public class Address {
private String provices;
private String city;
public void setAddress(String provices,String city){
this.provices = provices;
this.city = city;
}
@Override
public String toString() {
return "Address [provices=" + provices + ", city=" + city + "]";
}
}
这是一个我们要进行赋值的原始类 Person。下面我们产生一个 Person 对象,并调用其 clone 方法复制一个新的对象。
注意:调用对象的 clone 方法,必须要让类实现 Cloneable 接口,并且覆写 clone 方法。
测试:
@Test
public void testShallowClone() throws Exception{
Person p1 = new Person("zhangsan",21);
p1.setAddress("湖北省", "武汉市");
Person p2 = (Person) p1.clone();
System.out.println("p1:"+p1);
System.out.println("p1.getPname:"+p1.getPname().hashCode());
System.out.println("p2:"+p2);
System.out.println("p2.getPname:"+p2.getPname().hashCode());
p1.display("p1");
p2.display("p2");
p2.setAddress("湖北省", "荆州市");
System.out.println("将复制之后的对象地址修改:");
p1.display("p1");
p2.display("p2");
}
定义: 原型模式(Prototype Design Pattern)用一个已经创建的实例作为原型,通过复制该原型对象来创建一个和原型对象相同的新对象。
西游记中的孙悟空 拔毛变小猴,孙悟空这种根据自己的形状复制出多个身外化身的技巧,在面向对象软件设计领域被称为原型模式.孙悟空就是原型对象.
测试
@Test
public void test01() throws CloneNotSupportedException {
ConcretePrototype c1 = new ConcretePrototype();
ConcretePrototype c2 = c1.clone();
//输出false
System.out.println("对象c1和c2是同一个对象?" + (c1==c2));
}
/**
* 具体原型类
* 实现Cloneable标识接口,表示当前类对象可复制
**/
public class ConcretePrototype implements Cloneable, Serializable {
private Person person;
public Person getPerson() {
return person;
}
public void setPerson(Person person) {
this.person = person;
}
public void show(){
System.out.println("嫌疑人姓名: " + person.getName());
}
public ConcretePrototype() {
System.out.println("具体原型对象创建成功!");
}
@Override
protected ConcretePrototype clone() throws CloneNotSupportedException {
System.out.println("克隆对象复制成功!");
return (ConcretePrototype) super.clone();
}
}
import java.io.Serializable;
public class Person implements Serializable {
private String name;
public Person() {
}
public Person(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
//浅拷贝
@Test
public void test02() throws CloneNotSupportedException {
ConcretePrototype c1 = new ConcretePrototype();
Person p1 = new Person("峰哥");
c1.setPerson(p1);
//复制c1
ConcretePrototype c2 = c1.clone();
Person p2 = c2.getPerson();
p2.setName("凡哥");
c1.show();
c2.show();
//输出true,克隆对象和原型对象还是共享name属性的
System.out.println("对象p1和对象p2是同一个对象吗?" + (p1 == p2));
}
如果有需求场景中不允许共享同一对象,那么就需要使用深拷贝,如果想要进行深拷贝需要使用到对象序列化流 (对象序列化之后,再进行反序列化获取到的是不同对象).
/**
* 广告模板
**/
public class AdvTemplate {
//广告信名称
private String advSubject = "xx银行本月还款达标,可抽iPhone 13等好礼!";
//广告信内容
private String advContext = "达标用户请在2022年3月1日到2022年3月30日参与抽奖......";
public String getAdvSubject() {
return advSubject;
}
public void setAdvSubject(String advSubject) {
this.advSubject = advSubject;
}
public String getAdvContext() {
return advContext;
}
public void setAdvContext(String advContext) {
this.advContext = advContext;
}
}
邮件类代码
/**
* 邮件类
**/
public class Mail {
private String receiver; //收件人
private String subject; //邮件名称
private String appellation; //称呼
private String context; //邮件内容
private String tail; //邮件尾部 "xx版权所有"
public Mail(AdvTemplate advTemplate) {
this.subject = advTemplate.getAdvSubject();
this.context = advTemplate.getAdvContext();
}
public String getReceiver() {
return receiver;
}
public void setReceiver(String receiver) {
this.receiver = receiver;
}
public String getSubject() {
return subject;
}
public void setSubject(String subject) {
this.subject = subject;
}
public String getAppellation() {
return appellation;
}
public void setAppellation(String appellation) {
this.appellation = appellation;
}
public String getContext() {
return context;
}
public void setContext(String context) {
this.context = context;
}
public String getTail() {
return tail;
}
public void setTail(String tail) {
this.tail = tail;
}
}
客户类
/**
* 业务场景
**/
public class Client {
//发送邮件的数量
private static int MAX_COUNT = 6;
//发送邮件
public static void sendMail(Mail mail){
System.out.println("标题: " + mail.getSubject() + "\t 收件人: " + mail.getReceiver()
+ "\t ...发送成功!");
}
public static void main(String[] args) {
int i = 0;
//定义模板
Mail mail = new Mail(new AdvTemplate());
mail.setTail("xxx银行版权所有");
while(i < MAX_COUNT){
//每封邮件不同的信息
mail.setAppellation(" 先生 (女士)");
Random random = new Random();
int num = random.nextInt(999999999);
mail.setReceiver(num + "@"+"mashibing.com");
//发送邮件
sendMail(mail);
i++;
}
}
}
代码重构
Mail类
public class Mail implements Cloneable {
private String receiver; //收件人
private String subject; //邮件名称
private String appellation; //称呼
private String context; //邮件内容
private String tail; //邮件尾部 "xx版权所有"
public Mail(AdvTemplate advTemplate) {
this.subject = advTemplate.getAdvSubject();
this.context = advTemplate.getAdvContext();
}
public String getReceiver() {
return receiver;
}
public void setReceiver(String receiver) {
this.receiver = receiver;
}
public String getSubject() {
return subject;
}
public void setSubject(String subject) {
this.subject = subject;
}
public String getAppellation() {
return appellation;
}
public void setAppellation(String appellation) {
this.appellation = appellation;
}
public String getContext() {
return context;
}
public void setContext(String context) {
this.context = context;
}
public String getTail() {
return tail;
}
public void setTail(String tail) {
this.tail = tail;
}
@Override
protected Mail clone() {
Mail mail = null;
try {
return (Mail)super.clone();
} catch (CloneNotSupportedException e) {
e.printStackTrace();
return null;
}
}
}
Client类
public class Client {
//发送邮件的数量
private static int MAX_COUNT = 6;
//发送邮件
public static void sendMail(Mail mail){
System.out.println("标题: " + mail.getSubject() + "\t 收件人: " + mail.getReceiver()
+ "\t ...发送成功!");
}
public static void main(String[] args) {
int i = 0;
//定义模板
Mail mail = new Mail(new AdvTemplate());
mail.setTail("xxx银行版权所有");
while(i < MAX_COUNT){
//每封邮件不同的信息
Mail cloneMail = mail.clone();
cloneMail.setAppellation(" 先生 (女士)");
Random random = new Random();
int num = random.nextInt(999999999);
cloneMail.setReceiver(num + "@"+"mashibing.com");
//发送邮件
sendMail(cloneMail);
i++;
}
}
}
原型模式的优点:
原型模式缺点:
需要为每一个类配备一个克隆方法,而且该克隆方法位于一个类的内部,当对已有的类进行改造时需要修改源代码,违背了开闭原则.