赞
踩
上期回顾:建造者模式
原型模式是一种创建型设计模式,Prototype模式允许一个对象再创建另外一个可定制的对象,根本无需知道任何如何创建的细节,工作原理是:通过将一个原型对象传给那个要发动创建的对象,这个要发动创建的对象通过请求原型对象拷贝它们自己来实施创建。
原型模式有以下三种角色:
1.抽象原型类(Prototype):规定了具体原型对象必须实现的接口。
2.具体原型类(Realizetype):实现抽象原型类的 clone() 方法,它是可被复制的对象。
3.访问类(PrototypeTest):使用具体原型类中的 clone() 方法来复制新的对象
原型模式的克隆可以分为浅克隆
和深克隆
浅克隆:创建一个新对象,新对象的属性和原来对象完全相同,对于非基本类型属性,仍指向原有属性所指向的对象的内存地址。
深克隆:创建一个新对象,属性中引用的其他对象也会被克隆,不再指向原有对象地址。
Java中的Object类中提供clone()
方法来实现浅克隆。Cloneable
接口就是抽象原型类,而实现了Cloneable接口的子实现类就是具体的原型类。
原型模式设计如下:
//在这里抽象原型类是Cloneable接口 //具体原型类 public class Realizetype implements Cloneable{ public Realizetype() { System.out.println("具体原型对象已创建成功!"); } @Override protected Realizetype clone() throws CloneNotSupportedException { System.out.println("具体原型已复制成功!"); return (Realizetype) super.clone(); } } //访问类 public class Client { public static void main(String[] args) throws CloneNotSupportedException { //创建一个原型类对象 Realizetype realizetype = new Realizetype(); //得到原型对象的克隆对象 Realizetype clone = realizetype.clone(); //查看是否是同一个对象 System.out.println("原型对象和克隆的对象是否是同一个对象:"+(realizetype == clone)); } }
测试结果:
具体原型对象已创建成功!
具体原型已复制成功!
原型对象和克隆的对象是否是同一个对象:false
这里展示的原型模式只是一个简单的克隆,抽象原型类就是Cloneable接口
,具体原型类就是可被复制的对象
,而访问类就是使用克隆clone()来复制新的对象
。
需要注意的是,单看测试结果我们会发现原型模式有以下几个问题。
而且查看clone()源码发现它是一个native本地方法,也是说它并不是由java语言实现的。
protected native Object clone() throws CloneNotSupportedException;
用原型模式来实现以下需求:
假如某学校要对这一学期的社团指导老师颁发聘书,那么我现在就可以根据奖状模板进行克隆,用原型模式设计思想实现。
原型对象:奖状类
//原型对象:聘书 public class Letter implements Cloneable{ private String name; //指导老师姓名 public String getName() { return name; } public void setName(String name) { this.name = name; } public void show(){ System.out.println("兹聘请 "+name+" 老师为 xxx 社团指导老师!"); } @Override protected Letter clone() throws CloneNotSupportedException { return (Letter)super.clone(); } }
访问类:测试
public class Test {
public static void main(String[] args) throws CloneNotSupportedException {
Letter letter = new Letter();
//开始克隆
Letter letter1 = letter.clone();
//原型对象
System.out.print("原型对象:");
letter.setName("张三");
letter.show();
//克隆对象
System.out.print("克隆后对象:");
letter1.setName("李四");
letter1.show();
}
}
运行结果:
原型对象: 兹聘请 张三 老师为 xxx 社团指导老师!
克隆后对象: 兹聘请 李四 老师为 xxx 社团指导老师!
这个例子比较简单好理解,现在我在这个案例中引入深克隆和浅克隆
的概念。
浅克隆:创建一个新对象,新对象的属性和原来对象完全相同,对于非基本类型属性,仍指向原有属性所指向的对象的内存地址。
1、首先我再创建一个社团类,里面有一个社团名称的属性
//社团类 public class Association { private String name ; //社团名称 public Association(String name) { this.name = name; } public String getName() { return name; } public void setName(String name) { this.name = name; } }
2、修改聘书类,加上一个社团类的对象属性
//原型对象:聘书 public class Letter implements Cloneable{ private String name; //指导老师姓名 private Association association; //社团 public Letter(String name, Association association) { this.name = name; this.association = association; } public String getName() { return name; } public void setName(String name) { this.name = name; } public Association getAssociation() { return association; } public void setAssociation(Association association) { this.association = association; } public void show(){ System.out.println("兹聘请 "+name+" 老师为 "+association.getName()+" 社团指导老师!"); } @Override protected Letter clone() throws CloneNotSupportedException { return (Letter)super.clone(); } }
测试类:将原型对象聘书类进行克隆,然后打印出原型对象中Association引用地址和克隆后Association引用地址,判断该引用地址是否发生改变
public class Test { public static void main(String[] args) throws CloneNotSupportedException { Association association = new Association("歌舞社"); //拷贝之前的对象 Letter letter = new Letter("张三",association); System.out.print("克隆前对象:"); letter.show(); //拷贝之后的对象 Letter letter1 = letter.clone(); System.out.print("克隆后对象:"); letter1.show(); //判断 System.out.println(letter.getAssociation() == letter1.getAssociation()); System.out.println("克隆前Association对象的hashCode:"+letter.getAssociation().hashCode()); System.out.println("克隆后Association对象的hashCode:"+letter1.getAssociation().hashCode()); } }
输出结果:
克隆前对象:兹聘请 张三 老师为 歌舞社 社团指导老师!
克隆后对象:兹聘请 张三 老师为 歌舞社 社团指导老师!
true
克隆前Association对象的hashCode:666988784
克隆后Association对象的hashCode:666988784
结果发现,letter.getAssociation() == letter1.getAssociation()结果返回true
,说明克隆前后Letter
类中持有的Association
引用地址没有发生改变,加上show()内容一样,说明属性也没发生改变,这明显符合我们对浅克隆的定义。而且打印出来的hashCode
值相同,同样证明了它们是引用的同一个地址。
由于指向同一个地址,那我对克隆后的对象修改属性,原来的原型对象中的属性也会发生变化吗?
测试如下,将克隆的对象中原来的张三指导的“歌舞社”改为“足球社”,再查看未克隆前的原型对象是否发生了改变。
public class Test { public static void main(String[] args) throws CloneNotSupportedException { Association association = new Association("歌舞社"); Letter letter = new Letter("张三",association); //原来 System.out.println("原对象:"); letter.show(); //克隆并修改 Letter clone = letter.clone(); Association association1 = clone.getAssociation(); association1.setName("足球社"); //再次查看原来对象 System.out.println("克隆对象修改后再次查看原来对象:"); letter.show(); } }
测试结果:
原对象:
兹聘请 张三 老师为 歌舞社 社团指导老师!
克隆对象修改后再次查看原来对象:
兹聘请 张三 老师为 足球社 社团指导老师!
发现修改克隆对象中的属性,原对象也发生了同步改变。这再一次说明了浅克隆中,会把原型对象中成员变量为引用类型的引用地址也复制给克隆对象,且此引用对象的地址是共享给原型对象和克隆对象的。
总结:浅克隆只复制指向某个对象的引用,而不复制对象本身,新旧对象还是共享同一块内存,修改对象会改到原对象
清楚了浅克隆例子后。再来回顾一下深克隆的定义 ---- 创建一个新对象,属性中引用的其他对象也会被克隆,不再指向原有对象地址。
这要如何实现?很简单>
让Association类实现Cloneable接口,重写clone()方法
@Override
protected Association clone() throws CloneNotSupportedException {
return (Association) super.clone();
}
修改Letter类的clone()方法,加上对association的克隆
@Override
protected Letter clone() throws CloneNotSupportedException {
Letter letter = (Letter)super.clone();
//拷贝Association对象
letter.association= this.association.clone();
return letter;
}
测试结果:
原对象:
兹聘请 张三 老师为 歌舞社 社团指导老师!
克隆后对象:
兹聘请 张三 老师为 足球社 社团指导老师!
克隆对象修改后再次查看原来对象:
兹聘请 张三 老师为 歌舞社 社团指导老师!
经过这一顿操作后,修改克隆后对象的属性,也不会影响到原对象,这就是深克隆,它将原型对象中的所有类型,无论是值类型还是引用类型,都复制了一份给克隆对象。
总结:深克隆对原型对象完全拷贝,但新对象跟原对象不共享内存,修改新对象不会改变原对象。
最后用一张图简单的总结一下浅克隆和深克隆的区别
原型模式(Prototype Pattern)是用于创建重复的对象,同时又能保证性能。这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。
它的使用场景如下:
1、资源优化场景。
2、类初始化需要消化非常多的资源,这个资源包括数据、硬件资源等。
3、性能和安全要求的场景。
4、通过new 产生一个对象需要非常繁琐的数据准备或访问权限,则可以使用原型模式。
5、一个对象多个修改者的场景。
6、一个对象需要提供给其他对象访问,而且各个调用者可能都需要修改其值时,可以考虑使用原型模式拷贝多个对象供调用者使用。
7、在实际项目中,原型模式很少单独出现,一般是和工厂方法模式一起出现,通过 clone 的方法创建一个对象,然后由工厂方法提供给调用者。原型模式已经与 Java 融为浑然一体,大家可以随手拿来使用。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。