原型模式应用场景
原型设计模式一般使用在对对象进行复制的情况下,比如说一个班级里面有五十个人,每个人都是一个独立Person类的情况下,那么你需要实力话五十个Person类型的情况,一来比较耗时,二来执行占用内存效率不是很高。如果使用原型设计模式的情况下,那么只要对person进行clone,并赋值对应名字(属性)就可以区分每个类对应不同的同学。
Java的赋值类型分为值赋值和引用赋值,比如Int,char,byte等简单数据类型属于值赋值,而引用类型包括类,接口,数组等复杂类型。下面我们看看浅克隆和深克隆的区别。
浅克隆
在浅克隆中,我们直接将类型里面的值应用复制过来,成为一个独立的一份子。如果clone类的属性里面有引用类型的话,那么浅克隆clone来的属性值就是原属性的引用地址。那么虽然clone出来的类是独立的,那么其中引用属性的值是与原生类是共用的。两个类任意一方修改了引用类型的属性,那么双方都会随之改变。
深克隆
在深克隆中,无论原型对象是引用类型还是值类型,我们都会对其进行克隆。如果类中含有引用类型属性,那么对这个引用属性也会进行一个clone。在Java中我们可以通过序列化(Serialization)的方式来实现,序列化就是将对象写到流的过程,写到流中的对象是原有对象的一个拷贝,而原有对象依然存在在内存中。
原型模式示例
原型接口类(非必要)
package com.demo.test.prototype.iml;
import java.io.IOException;
import java.io.OptionalDataException;
public interface Prototype {
Prototype getShallowClone()throws CloneNotSupportedException;
Prototype getDeepClone()throws IOException,ClassNotFoundException;
}
原型类
package com.demo.test.prototype;
import com.demo.test.prototype.iml.Prototype;
import java.io.*;
public class Person implements Prototype, Serializable,Cloneable {
private String name;
private String age;
private WorkLog workLog;
public void setAge(String age) {
this.age = age;
}
public void setName(String name) {
this.name = name;
}
public String getAge() {
return age;
}
public String getName() {
return name;
}
public void setWorkLog(WorkLog workLog) {
this.workLog = workLog;
}
public WorkLog getWorkLog() {
return workLog;
}
@Override
public Prototype getShallowClone() throws CloneNotSupportedException {
Object object = null;
try{
object = super.clone();
}catch (CloneNotSupportedException e){
System.out.println(e);
return null;
}
return (Prototype)object;
}
@Override
public Prototype getDeepClone() throws IOException,ClassNotFoundException {
ByteArrayOutputStream bao = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(bao);
oos.writeObject(this);
ByteArrayInputStream bis = new ByteArrayInputStream(bao.toByteArray());
ObjectInputStream ois = new ObjectInputStream(bis);
return (Prototype) ois.readObject();
}
}
原型类属性worklog
package com.demo.test.prototype;
import java.io.Serializable;
public class WorkLog implements Serializable {
private String company;
private String time;
public void setCompany(String company) {
this.company = company;
}
public void setTime(String time) {
this.time = time;
}
public String getCompany() {
return company;
}
public String getTime() {
return time;
}
}
客户端类
package com.demo.test.prototype;
import com.demo.test.prototype.iml.Prototype;
import java.io.IOException;
import java.util.Date;
public class Client {
public static void main(){
Person person = new Person();
WorkLog workLog = new WorkLog();
workLog.setCompany("yd");
workLog.setTime(new Date().toString());
person.setName("sun");
person.setAge("29");
person.setWorkLog(workLog);
try{
//浅克隆
Person wPerson = (Person)person.getShallowClone();
wPerson.setName("wang");
wPerson.setAge("26");
//深克隆
Person zPerson = (Person)person.getDeepClone();
zPerson.setName("zheng");
//修改person工作
person.getWorkLog().setCompany("ali");
System.out.println("person.name:"+person.getName()+",--person.age:"+person.getAge()+",--person.work.company:"+person.getWorkLog().getCompany()+",--person.work.date:"+person.getWorkLog().getTime());
System.out.println("wPerson.name:"+wPerson.getName()+",--wPerson.age:"+wPerson.getAge()+",--wPerson.work.company:"+wPerson.getWorkLog().getCompany()+",--wPerson.work.date:"+wPerson.getWorkLog().getTime());
System.out.println("zPerson.name:"+zPerson.getName()+",--zPerson.age:"+zPerson.getAge()+",--zPerson.work.company:"+zPerson.getWorkLog().getCompany()+",--zPerson.work.date:"+zPerson.getWorkLog().getTime());
}catch (Exception e){
e.printStackTrace();
}
}
}
//person.name:sun,--person.age:29,--person.work.company:ali,--person.work.date:Tue Dec 14 22:10:29 CST 2021
//wPerson.name:wang,--wPerson.age:26,--wPerson.work.company:ali,--wPerson.work.date:Tue Dec 14 22:10:29 CST 2021
//zPerson.name:zheng,--zPerson.age:29,--zPerson.work.company:yd,--zPerson.work.date:Tue Dec 14 22:10:29 CST 2021
从上面的输出结果可以看书,在使用浅克隆修改的workLog.company属性的时候,两者都会一起修改。而深克隆的的没有收到任何影响。
tips
- 使用深克隆的时候,作为原型的属性也需要继承
Serializable
接口,否则序列化会报错。