赞
踩
推荐:Java设计模式汇总
定义
使用原型实例指定待创建的类型,并且通过复制这个原型实例来创建新的对象。
类型
创建型。
例子
Mail类(邮件类),实现了Cloneable接口
(只有实现了该接口的类的实例才可以调用clone()方法克隆实例,否则会抛出异常)。
package com.kaven.design.pattern.creational.prototype; 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) { this.content = content; } @Override public String toString() { return "Mail{" + "name='" + name + '\'' + ", emailAddress='" + emailAddress + '\'' + ", content='" + content + '\'' + '}'; } @Override protected Object clone() throws CloneNotSupportedException { System.out.println("clone mail object"); return super.clone(); } }
Cloneable接口
只是起到告诉程序这个类的实例可以调用clone方法的作用,它本身并没有定义任何方法。
源码如下:
package java.lang;
public interface Cloneable {
}
MailUtil类(邮件的工具类),调用sendMail()方法
可以发送邮件。
package com.kaven.design.pattern.creational.prototype;
import java.text.MessageFormat;
public class MailUtil {
public static void sendMail(Mail mail){
String outputContent = "向 {0} 同学,邮件地址:{1},邮件内容:{2},发送邮件成功";
System.out.println(MessageFormat.format(outputContent,mail.getName(),mail.getEmailAddress(),mail.getContent()));
}
}
应用层代码:
package com.kaven.design.pattern.creational.prototype; public class Test { private static String[] name = {"yy","kaven","jack","jojo"}; public static void main(String[] args) throws CloneNotSupportedException { Mail mail = new Mail(); mail.setContent("初始化模板"); for (int i = 0; i < name.length; i++) { Mail mailTemp = (Mail) mail.clone(); mailTemp.setName(name[i]); mailTemp.setEmailAddress(name[i]+"@lkwyy.com"); mailTemp.setContent("恭喜您,此次活动中奖了"); MailUtil.sendMail(mailTemp); } } }
结果:
Mail Class Constructor
clone mail object
向 yy 同学,邮件地址:yy@lkwyy.com,邮件内容:恭喜您,此次活动中奖了,发送邮件成功
clone mail object
向 kaven 同学,邮件地址:kaven@lkwyy.com,邮件内容:恭喜您,此次活动中奖了,发送邮件成功
clone mail object
向 jack 同学,邮件地址:jack@lkwyy.com,邮件内容:恭喜您,此次活动中奖了,发送邮件成功
clone mail object
向 jojo 同学,邮件地址:jojo@lkwyy.com,邮件内容:恭喜您,此次活动中奖了,发送邮件成功
Mail mail = new Mail()
会调用构造器创建原型实例,调用构造器时会输出Mail Class Constructor
,而每次要发送的邮件都是对这个原型实例的克隆Mail mailTemp = (Mail) mail.clone();
,并且可以发现clone()方法不会调用构造器
,对象拷贝时构造器是不会被执行的,Object类的clone()方法的原理是从内存中(具体的说,就是堆内存中)以二进制流的方式进行拷贝,并且会重新分配一个内存块(也是堆内存中),也就不需要调用构造器了
。
这里我们便实现了一个简单的原型模式例子,接下来我们分析一下克隆的问题。
深克隆与浅克隆
package com.kaven.design.pattern.creational.prototype.clone; import java.util.Date; 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 public String toString() { return "Pig{" + "name='" + name + '\'' + ", birthday=" + birthday + '}'; } @Override protected Object clone() throws CloneNotSupportedException { return super.clone(); } }
测试1:
package com.kaven.design.pattern.creational.prototype.clone;
import java.util.Date;
public class Test {
public static void main(String[] args) throws CloneNotSupportedException {
Pig pig = new Pig("佩奇",new Date(0L));
Pig clonePig = (Pig) pig.clone();
System.out.println(pig);
System.out.println(clonePig);
System.out.println(pig == clonePig);
}
}
输出:
Pig{name='佩奇', birthday=Thu Jan 01 08:00:00 CST 1970}
Pig{name='佩奇', birthday=Thu Jan 01 08:00:00 CST 1970}
false
很显然它们不是指向同一个实例,因为会重新分配一个内存块。
测试2:
package com.kaven.design.pattern.creational.prototype.clone; import java.util.Date; public class Test { public static void main(String[] args) throws CloneNotSupportedException { Pig pig = new Pig("佩奇",new Date(0L)); Pig clonePig = (Pig) pig.clone(); System.out.println(pig); System.out.println(clonePig); clonePig.getBirthday().setTime(1000000000000L); System.out.println(pig); System.out.println(clonePig); } }
输出:
Pig{name='佩奇', birthday=Thu Jan 01 08:00:00 CST 1970}
Pig{name='佩奇', birthday=Thu Jan 01 08:00:00 CST 1970}
Pig{name='佩奇', birthday=Sun Sep 09 09:46:40 CST 2001}
Pig{name='佩奇', birthday=Sun Sep 09 09:46:40 CST 2001}
为什么我们改变clonePig的birthday属性,pig的birthday属性也会进行改变?
我们来Debug一下。
打上断点:
开始Debug。
Debug后,可以发现pig(Pig@616)
和clonePig(Pig@617)
确实不是指向同一个实例,但它们的birthday属性却是指向相同的Date实例(Date@621)
,这就是浅克隆,所以,当修改clonePig的birthday属性时,pig的birthday属性也会进行改变。
从上图可以看出,当修改了clonePig的birthday属性时clonePig.getBirthday().setTime(1000000000000L)
,pig的birthday属性也会进行改变。
如何改进成深克隆呢?
改进如下:
package com.kaven.design.pattern.creational.prototype.clone; import java.util.Date; 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 public String toString() { return "Pig{" + "name='" + name + '\'' + ", birthday=" + birthday + '}'; } @Override protected Object clone() throws CloneNotSupportedException { Pig clonePig = (Pig) super.clone(); //深克隆 clonePig.birthday = (Date) clonePig.birthday.clone(); return clonePig; } }
测试:
package com.kaven.design.pattern.creational.prototype.clone; import java.util.Date; public class Test { public static void main(String[] args) throws CloneNotSupportedException { Pig pig = new Pig("佩奇",new Date(0L)); Pig clonePig = (Pig) pig.clone(); System.out.println(pig); System.out.println(clonePig); clonePig.getBirthday().setTime(1000000000000L); System.out.println(pig); System.out.println(clonePig); } }
结果:
Pig{name='佩奇', birthday=Thu Jan 01 08:00:00 CST 1970}
Pig{name='佩奇', birthday=Thu Jan 01 08:00:00 CST 1970}
Pig{name='佩奇', birthday=Thu Jan 01 08:00:00 CST 1970}
Pig{name='佩奇', birthday=Sun Sep 09 09:46:40 CST 2001}
从测试结果上来看,当修改clonePig的birthday属性时,pig的birthday属性并没有进行改变。
这就改进成深克隆了吗?是的,这便是深克隆。
其实我们还依赖了Date类本身就实现了深克隆
,我们才会如此方便,Date类的clone()方法源码如下:
public Object clone() {
Date d = null;
try {
d = (Date)super.clone();
if (cdate != null) {
d.cdate = (BaseCalendar.Date) cdate.clone();
}
} catch (CloneNotSupportedException e) {} // Won't happen
return d;
}
有些引用型没有这种浅克隆的问题,如String或Integer等包装类型
,大家可以自己试一试。
还有就是,final修饰的成员变量是不能进行深度拷贝的
。
适用场景
优点
缺点
如果有说错的地方,请大家不吝赐教(记得留言哦~~~~)。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。