定义
- 用一个已经创建的实例作为原型,通过复制该原型对象来创建一个和原型相同或相似的新对象
- 这种模式存在的应用场景在于,能够复制当前对象,实现对象数据的克隆
需求
????批量发送邮件
实现方式 1
创建 Mail.java
/**
* @author BNTang
*/
public class Mail {
private String name;
private String address;
private String content;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
public String getContent() {
return content;
}
public void setContent(String content) {
this.content = content;
}
public Mail() {
System.out.println("创建邮件");
}
public String toString() {
return "Mail{" + "name='" + name + '\'' + ", address='" + address + '\'' + ", content='" + content + '\'' + '}';
}
}
创建 MailUtil.java
/**
* @author BNTang
*/
public class MailUtil {
public static void sendMail(Mail mail) {
String res = "收件人:" + mail.getName() + "邮件地址" + mail.getAddress() + "发送内容" + mail.getContent();
System.out.println(res);
}
public static void saveOriginMail(Mail mail) {
System.out.println("------------------");
System.out.println(mail);
}
}
创建 Client.java
/**
* @author BNTang
**/
public class Client {
public static void main(String[] args) {
Mail mail = new Mail();
mail.setContent("原始模板内容");
MailUtil.saveOriginMail(mail);
for (int i = 1; i <= 10; i++) {
mail.setName("姓名: 张三 " + i);
mail.setAddress("地址: 北京 " + i);
mail.setContent("内容: " + i);
System.out.println(mail);
}
MailUtil.saveOriginMail(mail);
}
}
- 直接这样发送没有办法保存原始模板,最后得到的是最后一条邮件的内容
采用原型模式
- 在 Mail 类中实现
cloneable
接口,覆盖clone
方法
这个代码我就不贴了自行添加即可,其它修改的内容如下图,然后再次运行测试类代码
/**
* @author BNTang
**/
public class Client {
public static void main(String[] args) throws Exception {
Mail mail = new Mail();
mail.setContent("原始模板内容");
MailUtil.saveOriginMail(mail);
for (int i = 1; i <= 10; i++) {
Mail cloneMain = (Mail)mail.clone();
cloneMain.setName("姓名: 张三 " + i);
cloneMain.setAddress("地址: 北京 " + i);
cloneMain.setContent("内容: " + i);
System.out.println(cloneMain);
}
MailUtil.saveOriginMail(mail);
}
}
实现方式 3
定义抽象原型
- 创建
ProtoType.java
抽象类
/**
* @author BNTang
**/
public abstract class ProtoType implements Cloneable {
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
}
继承原型
直接运行测试类代码即可,效果如下图所示
ProtoType 类需要具备以下两个条件
实现 Cloneable 接口
- 在 Java 语言有一个
Cloneable
接口,它的作用只有一个,就是在运行时通知虚拟机可以安全地在实现了此接口的类上使用clone
方法 - 在 Java 虚拟机中,只有实现了这个接口的类才可以被拷贝,否则在运行时会抛出
CloneNotSupportedException
异常
重写 Object 类中的 clone 方法
- Java 中,所有类的父类都是
Object
类 - Object 类中有一个
clone
方法,作用是返回对象的一个拷贝,但是其作用域是protected
类型的,一般的类无法调用 - 因此,
ProtoType
类需要将clone
方法的作用域修改为public
类型
UML 图
深拷贝与浅拷贝
浅拷贝
- 浅拷贝只复制指向某个对象的指针,而不复制对象本身,新旧对象还是共享同一块内存
深拷贝
- 创建一个新对象,属性中引用的其他对象也会被克隆,不再指向原有对象地址
????String 要不要深拷贝呢
- String 类型是存在常量池当中的,可参考:
原型实现深拷贝
- 创建 Person.java
/**
* @author BNTang
*/
public class Person implements Cloneable {
private Integer age;
private String name;
private Date birthday;
public Object clone() throws CloneNotSupportedException {
Person person = (Person)super.clone();
// Date 是系统的类,不需要我们重写 clone 方法
person.birthday = (Date)person.birthday.clone();
return person;
}
public String toString() {
return "Person{" + "age=" + age + ", name='" + name + '\'' + ", birthday=" + birthday + '}';
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Date getBirthday() {
return birthday;
}
public void setBirthday(Date birthday) {
this.birthday = birthday;
}
}
- 创建 PersonClient.java
/**
* @author BNTang
**/
public class PersonClient {
public static void main(String[] args) throws CloneNotSupportedException {
Person person = new Person();
person.setAge(10);
person.setName("BNTang");
person.setBirthday(new Date());
System.out.println(person);
Person clonePerson = (Person)person.clone();
clonePerson.getBirthday().setTime(66666666666L);
clonePerson.setName("JonathanLee");
System.out.println(clonePerson);
System.out.println(person);
}
}
运行测试类代码,结果如下图所示