原型模式
用原型实例指定创建对象的种类,并且通过拷贝这个原型来创建新的对象
通过原型模式,绕过构造方法创建对象,利用内存直接拷贝对象,提高对象的创建性效率。在有大量的对象创建或者类初始化消耗多资源的场景下可以利用原型模式来优化。当然在实现的过程中,要注意浅拷贝与深拷贝的问题,防止写出 bug
浅拷贝和深拷贝 :
浅拷贝(浅克隆) :浅复制仅仅复制所考虑的对象,而不复制它所引用的对象。
深拷贝(深克隆) :深复制把要复制的对象所引用的对象都复制了一遍。
可以利用串行化来做深复制,所谓对象序列化就是将对象的状态转换成字节流,以后可以通过这些值再生成相同状态的对象。
java中的object的clone方法执行的是浅拷贝
覆盖Object中的clone方法, 实现深拷贝
为了要在clone对象时进行深拷贝, 那么就要Clonable接口,覆盖并实现clone方法,除了调用父类中的clone方法得到新的对象, 还要将该类中的引用变量也clone出来。如果只是用Object中默认的clone方法,是浅拷贝的
static class Body implements Cloneable{
public Head head;
public Body() {}
public Body(Head head) {this.head = head;}
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
}
static class Head /*implements Cloneable*/{
public Face face;
public Head() {}
public Head(Face face){this.face = face;}
}
public static void main(String[] args) throws CloneNotSupportedException {
Body body = new Body(new Head());
Body body1 = (Body) body.clone();
System.out.println("body == body1 : " + (body == body1) );
System.out.println("body.head == body1.head : " + (body.head == body1.head));
}
结果:
bodybody1: false
body.headbody1.head: true
对象的复制是深拷贝,但是对象的属性是浅拷贝
如果要使对象属性也是深拷贝,那么也要在对象body中引用的Head对象叶clone一次
static class Body implements Cloneable{
public Head head;
public Body() {}
public Body(Head head) {this.head = head;}
@Override
protected Object clone() throws CloneNotSupportedException {
Body newBody = (Body) super.clone();
newBody.head = (Head) head.clone();
return newBody;
}
}
static class Head implements Cloneable{
public Face face;
public Head() {}
public Head(Face face){this.face = face;}
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
}
public static void main(String[] args) throws CloneNotSupportedException {
Body body = new Body(new Head());
Body body1 = (Body) body.clone();
System.out.println("body == body1 : " + (body == body1) );
System.out.println("body.head == body1.head : " + (body.head == body1.head));
}
bodybody1: false
body.headbody1.head: false
这样clone对象后,对象里面的属性也会被深拷贝
使用new和clone方式创建对象
详解Java中的clone方法 – 原型模式_昨夜星辰_zhangjg的博客-CSDN博客_clone
- new操作符的本意是分配内存。程序执行到new操作符时, 首先去看new操作符后面的类型,因为知道了类型,才能知道要分配多大的内存空间。分配完内存之后,再调用构造函数,填充对象的各个域,这一步叫做对象的初始化,构造方法返回后,一个对象创建完毕,可以把他的引用(地址)发布到外部,在外部就可以使用这个引用操纵这个对象
- clone在第一步是和new相似的, 都是分配内存,调用clone方法时,分配的内存和源对象(即调用clone方法的对象)相同,然后再使用原对象中对应的各个域,填充新对象的域, 填充完成之后,clone方法返回,一个新的相同的对象被创建,同样可以把这个新对象的引用发布到外部
引用的复制
p和p1都只是一个对象的引用,都是指向同一个对象
Person p = new Person(23, "zhang");
Person p1 = p;
System.out.println(p);
System.out.println(p1);
对象的复制
p和p1是地址不同的两个对象,也就是创建了新的对象
Person p = new Person(23, "zhang");
Person p1 = (Person) p.clone();
System.out.println(p);
System.out.println(p1);
/**
* 如果需要短时间创建大量对象,并且new的过程比较耗时。则可以考虑使用原型模式!
*/
public class Client4 {
public static void main(String[] args) throws Exception {
testNew(1000);
testClone(1000);
}
public static void testNew(int size){
long start = System.currentTimeMillis();
for(int i=0;i<size;i++){
Laptop t = new Laptop();
}
long end = System.currentTimeMillis();
System.out.println("new的方式创建耗时:"+(end-start));
}
public static void testClone(int size) throws CloneNotSupportedException{
long start = System.currentTimeMillis();
Laptop t = new Laptop();
for(int i=0;i<size;i++){
Laptop temp = (Laptop) t.clone();
}
long end = System.currentTimeMillis();
System.out.println("clone的方式创建耗时:"+(end-start));
}
}
class Laptop implements Cloneable { //笔记本电脑
public Laptop() {
try {
Thread.sleep(10); //模拟创建对象耗时的过程!
} catch (InterruptedException e) {
e.printStackTrace();
}
}
@Override
protected Object clone() throws CloneNotSupportedException {
Object obj = super.clone(); //直接调用object对象的clone()方法!
return obj;
}
}
结果
new的方式创建耗时:15697
clone的方式创建耗时:15
所以,大量的对象创建比较耗时的,可以用原型模式
**应用场景 **:
- 原型模式很少单独出现,一般是和工厂方法模式一起出现,通过clone的方法创建一个对象,然后由工厂方法提供给调用者。
- spring中bean的创建实际就是两种:单例模式和原型模式。(当然,原型模式需要和工厂模式搭配起来)。
- 如果需要短时间创建大量对象,并且new的过程比较耗时。则可以考虑使用原型模式!
- 很多软件提供的CTRL+C和CTRL+V操作的就是原型模式的典型应用!
案例
-
创建工作经历类
import java.io.Serializable; /** * 工作经历实现序列化接口 */ public class WorkExperience implements Serializable { private static final long serialVersionUID = 1L; private String workDate; private String company; //提供getter/setter方法 public String getWorkDate() { return workDate; } public void setWorkDate(String workDate) { this.workDate = workDate; } public String getCompany() { return company; } public void setCompany(String company) { this.company = company; } }
-
创建人实体类
import java.io.*; public class People implements Serializable,Cloneable { private static final long serialVersionUID = 1L; private String name; private String sex; private Integer age; private WorkExperience work; public People(String name) { this.name = name; work = new WorkExperience(); } //设置个人信息 public void setPersonalInfo(String sex , Integer age) { this.sex=sex; this.age=age; } //设置工作经历 public void setWorkExperience(String workDate,String company) { work.setWorkDate(workDate); work.setCompany(company); } //显示 public void display(){ System.out.println(String.format("%s %s %s", name,sex,age)); System.out.println(String.format("工作经历:%s %s", work.getWorkDate(), work.getCompany())); } //浅复制 public Object clone(){ Object obj = null; try{ obj = super.clone(); }catch (CloneNotSupportedException e){ e.printStackTrace(); } return obj; } //深复制 public Object deepClone () throws IOException,ClassNotFoundException { //将对象写入流中,使用序列化和反序列化实现深复制 ByteArrayOutputStream bo = new ByteArrayOutputStream(); ObjectOutputStream oo = new ObjectOutputStream(bo); oo.writeObject(this); //从流中读出来 ByteArrayInputStream bi = new ByteArrayInputStream(bo.toByteArray()); ObjectInputStream oi = new ObjectInputStream(bi); return (oi.readObject()); } }
-
测试使用
/** * 测试类 */ public class TestPrototype { public static void main(String[] args) throws IOException,ClassNotFoundException { People p1 = new People("张三"); p1.setPersonalInfo("男",23); p1.setWorkExperience("2016-2019","百度"); People clone = (People) p1.clone(); //浅复制 clone.setPersonalInfo("男",20); clone.setWorkExperience("2017-2022","腾讯"); p1.display(); clone.display(); People p2 = new People("李四"); p2.setPersonalInfo("女",20); p2.setWorkExperience("2018-2020","美团"); People deepClone = (People)p2.deepClone(); //深复制 deepClone.setPersonalInfo("女",22); deepClone.setWorkExperience("2018-2019","百度外卖"); p2.display(); deepClone.display(); } }