1、基本概念
原型模式属于创造型模式,通过二进制流拷贝已有的对象。
原型模式有浅克隆和深度克隆
2、原型类型
2.1、浅克隆
案例:
原型接口Iprototype:
public interface Iprototype<T> { T clone(); }
原对象实现原型接口:
@Data public class Teacher implements Iprototype<Teacher>{ private String name; private Integer age; private String no; private String addr; @Override public Teacher clone() { Teacher teacher = new Teacher(); teacher.setName(this.name); teacher.setNo(this.no); teacher.setAge(this.age); teacher.setAddr(this.addr); return teacher; } }
测试类:
public class Test { public static void main(String[] args) { Teacher teacher = new Teacher(); teacher.setAddr("wuahn"); teacher.setAge(20); teacher.setName("lilei"); teacher.setNo("1001"); Teacher clone = teacher.clone(); System.out.println("原对象:"+teacher); System.out.println("克隆对象:"+clone); } }
输出:
原对象:Teacher(name=lilei, age=20, no=1001, addr=wuahn)
克隆对象:Teacher(name=lilei, age=20, no=1001, addr=wuahn)
这就是一个原型设计,我们自己手写的clone方法,如果属性过多,那么就需要设置很多属性,比较麻烦。
所以实际上我们开发上只需要实现Cloneable接口即可,是jdk为我们提供的。
我们需要覆写clone方法:
@Data public class Teacher implements Cloneable{ private String name; private Integer age; private String no; private String addr; @Override protected Teacher clone() { try{ return (Teacher)super.clone(); }catch (Exception e){ e.printStackTrace(); return null; } } }
测试输出的结果和上面一样。
上面的克隆是一种浅克隆,那么浅克隆有什么缺点?
再看一个案例:Teacher类新增一个属性classroms
@Data public class Teacher implements Cloneable{ private String name; private Integer age; private String no; private String addr; private List<String> classroms;
测试:
public class Test { public static void main(String[] args) { Teacher teacher = new Teacher(); teacher.setAddr("wuahn"); teacher.setAge(20); teacher.setName("lilei"); teacher.setNo("1001"); List<String> list = new ArrayList<>(); list.add("101教室"); list.add("102教室"); teacher.setClassroms(list); Teacher clone = teacher.clone(); clone.getClassroms().add("103教室"); System.out.println("原对象:"+teacher); System.out.println("克隆对象:"+clone); } }
输出:
原对象:Teacher(name=lilei, age=20, no=1001, addr=wuahn, classroms=[101教室, 102教室, 103教室])
克隆对象:Teacher(name=lilei, age=20, no=1001, addr=wuahn, classroms=[101教室, 102教室, 103教室])
当我们修改克隆对象的时候,会把原对象也修改掉,这肯定就有问题了,这也是浅克隆的问题了
当对象中引入了其他对象的时候,如集合,数组,Student等类型的属性时,克隆只能复制它们的地址。
所以它们还是指向同一内存空间。
这时候就需要深克隆来解决:使用序列化接口Serializable
2.2、深克隆
使用io流来进行操作。
@Data
public class Teacher implements Cloneable,Serializable{
private String name;
private Integer age;
private String no;
private String addr;
private List<String> classroms;
protected Teacher deepClone() {
try{
ByteArrayOutputStream bos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(bos);
oos.writeObject(this);
ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray());
ObjectInputStream ois = new ObjectInputStream(bis);
return (Teacher)ois.readObject();
}catch (Exception e){
e.printStackTrace();
return null;
}
}
@Override
protected Teacher clone() {
try{
return (Teacher)super.clone();
}catch (Exception e){
e.printStackTrace();
return null;
}
}
}
测试:
public class Test { public static void main(String[] args) { Teacher teacher = new Teacher(); teacher.setAddr("wuahn"); teacher.setAge(20); teacher.setName("lilei"); teacher.setNo("1001"); List<String> list = new ArrayList<>(); list.add("101教室"); list.add("102教室"); teacher.setClassroms(list); Teacher clone = teacher.deepClone(); clone.getClassroms().add("103教室"); System.out.println("原对象:"+teacher); System.out.println("克隆对象:"+clone); } }
输出:
原对象:Teacher(name=lilei, age=20, no=1001, addr=wuahn, classroms=[101教室, 102教室])
克隆对象:Teacher(name=lilei, age=20, no=1001, addr=wuahn, classroms=[101教室, 102教室, 103教室])
我们可以看到已经解决了浅克隆的问题。
注意点:深克隆会破坏单例模式,所以单例模式下不要去实现cloneable接口
在开发中,很多项目都提供了克隆工具,如Apache BeanUtils 和Spring BeanUtils
不推荐使用Apache BeanUtils,在阿里巴巴的泰山版开发手册中有提到: