一.介绍
1.定义:
指原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象
不需要知道任何创建的细节,不调用构造函数
2.类型:
创建型
3.适用场景:
类初始化消耗较多资源
new产生的一个对象需要非常繁琐的过程(数据准备,访问权限等)
构造函数比较复杂
循环体中生产大量对象时
4.优点:
原型模式性能比直接new一个对象性能高
简化创建过程
5.缺点:
必须配备克隆方法(cloneable接口)
对克隆复杂对象或对克隆出的对象进行复杂改造时,容易引入风险
深拷贝,浅拷贝要运用得当
6.注意:
深克隆和浅克隆是一个大坑
二.实例demo演示
1.创建一个邮件类,包括邮件的名字,邮件的地址,邮件的内容信息属性,但是为了显示效果,要实现Cloneable接口,并且覆盖clone方法
public class Mail implements Cloneable{
private String name;
private String emailAddress;
private String Content;
public Mail() { System.out.println("Mail Class Constructor..."); }
public String getName() { return name; }
public void setName(String name) { this.name = name; }
public String getEmailAddress() { return emailAddress; }
public void setEmailAddress(String emailAddress) { this.emailAddress = emailAddress; }
public String getContent() { return Content; }
public void setContent(String content) { Content = content; }
@Override
public String toString() {
return "Mail{" +
"name='" + name + '\'' +
", emailAddress='" + emailAddress + '\'' +
", Content='" + Content + '\'' +
'}'+super.toString();
}
@Override
protected Object clone() throws CloneNotSupportedException {
System.out.println("克隆方法...");
return super.clone();
}
}
2.创建一个邮件的工具类,里面有发送邮件和保存邮件方法
public class MailUtil {
/**
* 发送邮件
* @param mail
*/
public static void sendMail(Mail mail) {
String outputContent = "向{0}同学,邮件地址:{1},邮件内容:{2}发送邮件成功";
System.out.println(MessageFormat.format(outputContent,mail.getName(),mail.getEmailAddress(),mail.getContent()));
}
/**
* 存储原始文件
*
* @param mail
*/
public static void saveOriginMailRecord(Mail mail){
System.out.println("存储原始邮件: "+mail.getContent());
}
}
3.测试,具体的分析在代码注释中
public class Test {
public static void main(String[] args) throws CloneNotSupportedException {
/**
* 此处缺点:
* 虽然new对象是在for循环外面,但是最终返回的结果却不是初始化模板,因此推论就是对象还是被new了很多次.
* 因此是非常消耗性能的,解决方式:
* 在实体类中实现克隆Cloneable接口,并且覆盖其clone方法,以原型模式来解决性能问题
*
* 并在for循环中把已经new的对象进行克隆,使得每次调用之前会调用克隆方法进行克隆,掉方法相对于new对象对性能的影响还是挺小的
*
*
*/
Mail mail = new Mail();
mail.setContent("初始化模板");
System.out.println("初始化mail: "+mail);
for(int i=0;i<10;i++){
Mail mail1 = (Mail) mail.clone();
mail1.setName("姓名"+i);
mail1.setEmailAddress("姓名"+i+"@ligh.com");
mail1.setContent("恭喜你中奖了!");
MailUtil.sendMail(mail1);
System.out.println("克隆的mail: "+mail1);
}
MailUtil.saveOriginMailRecord(mail);
}
}
三.深克隆和浅克隆的坑
1.创建一个猪类,然后实现Cloneable接口,并覆盖clone方法,针对clone方法不同的操作来看深克隆和浅克隆的效果
public class Pig implements Cloneable{
private String name;
private Date birthday;
public Pig(String name, Date birthday) {
this.name = name;
this.birthday = birthday;
}
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;
}
@Override
protected Object clone() throws CloneNotSupportedException {
System.out.println("克隆方法...");
/**
* 直接return,是一种浅克隆的方式,导致的结果就是对被克隆的对象属性进行了修改,克隆之后的对象竟然该属性也进行了修改
* 这是不科学的,因为我只是在克隆后修改一个对象属性,你却能同步修改的内容
*/
// return super.clone();
/**
* 深克隆方式:
* 这种方式的目的就是在修改了被克隆对象的时候,不会影响克隆对象的属性的变化,也就是被克隆对象在克隆时属性的修改不会同步到克隆对象
*
*/
Pig pig = (Pig) super.clone();
pig.birthday = (Date)pig.birthday.clone();
return pig;
}
@Override
public String toString() {
return "Pig{" +
"name='" + name + '\'' +
", birthday=" + birthday +
'}'+super.toString();
}
}
2.测试:
public class Test {
public static void main(String[] args) throws Exception{
Date date = new Date(0L);
Pig pig1 = new Pig("佩奇", date);
Pig pig2 = (Pig) pig1.clone();
System.out.println(pig1);
System.out.println(pig2);
System.out.println();
/**
* 在克隆之后对远对象进行重新赋值操作
*/
pig1.getBirthday().setTime(66666666666L);
System.out.println(pig1);
System.out.println(pig2);
}
}
3.坑的总结:
上面的测试,在克隆之后对原对象进行属性的修改,如果是浅克隆的话,修改的属性会被同步到克隆的对象,这是不行的。也是一个原型的大坑,所以要在克隆方法中进行操作,使用深克隆的方式进行操作。原型模式如果想用的好的话必须要知道这个坑。