原型模式
需求:
简单来说,就是OA系统中,一个员工填写周报的内容,每个星期大多数位置内容相同需要修改的就只有少数部分。如果每个月都要全部进行填写,那么久又麻烦效率又低,故可以保存周报模板,只需要修改不同部分即可。
浅克隆
class Worker implements Cloneable{
private int id;
private String context;
private String sex;
private Date date;
//注意要将重写的clone方法的权限修饰符由protected改为public
@Override
public Object clone() throws CloneNotSupportedException {
return super.clone();
}
// .... 此处省略get,set,toString方法
public Worker(int id, String context, String sex, Date date) {
this.id = id;
this.context = context;
this.sex = sex;
this.date = date;
}
}
//上为服务端,下为客户端(我们角色为客户,可以随意修改客户端内容,却不能修改服务端内容)
//==============================================
public class AppTest {
public static void main(String[] args) throws CloneNotSupportedException {
//创建一组数据
Worker worker = new Worker(1, "刘备", "男", new Date());
System.out.println(worker);
//过一段时间后在想要一组数据,但是只修改想要修改的布冯,其余部分不用修改
Worker clone = (Worker) worker.clone();
clone.setContext("诸葛亮");
clone.setSex("女");
System.out.println(clone);
}
}
注意:
- 克隆会得到一个新的对象
- 克隆是直接复制内存中的二进制文件,不会引起全构造器的使用,效率更高
- 克隆出来的对象是两个不同的空间,他们的地址是不一样的
缺点:
克隆后,修改了一个对象的属性,那么另一个对象的属性也会发生变化
比如:如果这时候我们修改了里面的时间对象,如修改了代码:
那么结果:
原因: 浅拷贝就是将原来的对象的二进制文件原模原样的复制过来,包括二进制中的对象的内存地址也是原模原样的复制过来,如果修改了拷贝过来的对象中的属性对象的值,那么由于两个指向的地址相同,那么拷贝之前的对象内存的值也会发生改变。
浅拷贝详细模型图:
含义:原对象里面的对象属性原来执行的是内存地址为200的对象,浅拷贝将原对象二进制文件原模原样拷贝了过来,包括对象内指向200的地址,这时候,我们在拷贝出来的对象内修改对象内指向200的对象的属性,那么由于原对象内的对象属性也是指向200的内存地址,故值也会改变。
深拷贝
深拷贝代码修改(只是修改了重写的clone部分)
class Worker implements Cloneable{
private int id;
private String context;
private String sex;
private Date date;
//注意修改访问修饰符为public
@Override
public Object clone() throws CloneNotSupportedException {
//先克隆一份该对象
Worker clone = (Worker) super.clone();
//将对象中的对象也进行一次克隆
Date date = (Date) clone.getDate().clone();
//将克隆出来的属性传入拷贝出来的对象中
clone.setDate(date);
return clone;
}
// .... 此处省略get,set,toString方法
public Worker(int id, String context, String sex, Date date) {
this.id = id;
this.context = context;
this.sex = sex;
this.date = date;
}
}
//原型模式
//==============================================
public class AppTest {
public static void main(String[] args) throws CloneNotSupportedException {
//创建一组数据
Worker worker = new Worker(1, "刘备", "男", new Date());
System.out.println(worker);
//过一段时间后在想要一组数据,但是只修改想要修改的布冯,其余部分不用修改
Worker clone = (Worker) worker.clone();
clone.setContext("诸葛亮");
clone.setSex("女");
clone.getDate().setTime(0);
System.out.println(clone);
System.out.println(worker);
}
}
修改的部分只有clone里面的部分,我们在拷贝的时候,顺便将内部对象(Date)进行拷贝一份,并将它重新赋值给clone出来的对象。
拷贝详细模型图:
简单来说就是将对象内的对象也进行一次拷贝,并将拷贝出来的赋值给外面拷贝的对象,就不会产生对象内部的对象内存地址一样的问题。
缺点: 如果对象嵌套太深,则拷贝起来就很麻烦 (也就是说如果一个对象包含一个对象,里面的对象又包含一个对象…这样拷贝就得一个对象一个对象的进行拷贝)
改善(序列化方式拷贝)
序列化天生就是一种深拷贝
存于磁盘中的序列化拷贝
class Worker implements Cloneable,Serializable{
private int id;
private String context;
private String sex;
private Date date;
@Override
public Object clone() throws CloneNotSupportedException {
try {
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("F://o.txt"));
oos.writeObject(this);//序列化时,对象的所有层级关系会被自动处理
oos.close();
ObjectInputStream ois = new ObjectInputStream(new FileInputStream("F://o.txt"));
Object o = ois.readObject();
ois.close();
return o;
} catch (IOException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
return null;
}
//此处省略get,set,toString方法
public Worker(int id, String context, String sex, Date date) {
this.id = id;
this.context = context;
this.sex = sex;
this.date = date;
}
}
//原型模式
//==============================================
public class AppTest {
public static void main(String[] args) throws CloneNotSupportedException {
//创建一组数据
Worker worker = new Worker(1, "刘备", "男", new Date());
System.out.println(worker);
//过一段时间后在想要一组数据,但是只修改想要修改的布冯,其余部分不用修改
Worker clone = (Worker) worker.clone();
clone.setContext("诸葛亮");
clone.setSex("女");
clone.getDate().setTime(0);
System.out.println(clone);
System.out.println(worker);
}
}
序列化会自动处理对象内的层级关系
缺点: 程序中存在盘符,则与Windows耦合了,位置不应该写死,linux下面不存在盘符,所以该程序转到linux中不可用
存于内存中的序列化拷贝(我们推荐的原型模式的方式)
class Worker implements Cloneable,Serializable{
private int id;
private String context;
private String sex;
private Date date;
@Override
public Object clone() throws CloneNotSupportedException {
try {
ByteArrayOutputStream out = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(out);
oos.writeObject(this);
oos.close();
//从内存中取出数据
byte[] bytes = out.toByteArray();
ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(bytes));
Object o = ois.readObject();
ois.close();
return o;
} catch (IOException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
return null;
}
//
public Worker(int id, String context, String sex, Date date) {
this.id = id;
this.context = context;
this.sex = sex;
this.date = date;
}
}
//原型模式
//==============================================
public class AppTest {
public static void main(String[] args) throws CloneNotSupportedException {
//创建一组数据
Worker worker = new Worker(1, "刘备", "男", new Date());
System.out.println(worker);
//过一段时间后在想要一组数据,但是只修改想要修改的布冯,其余部分不用修改
Worker clone = (Worker) worker.clone();
clone.setContext("诸葛亮");
clone.setSex("女");
clone.getDate().setTime(0);
System.out.println(clone);
System.out.println(worker);
}
}
创建对象的四种方式:1、new 2、反射 3、序列化 4、克隆
以上内容为课后总结,如有侵权,请告知本人!