原型模式之深浅克隆
原型模式跟其他的创建型模式不同,它要做的是对特定的对象进行克隆。所谓克隆就是根据当前对象的特征,完全的复制一份出来。原型模式分为深拷贝和浅拷贝。不管是深拷贝和浅拷贝对于对象中的基本数据类型和String类型都会完全的复制,区别就是在复制对象中的其他引用类型时,浅拷贝只会复制对象中引用类型的地址,而不会完全的克隆一份。
浅拷贝
下面测试代码,User类中有Integer类型和String类型以及一个Father对象。User类对外提供的copy()方法使用了Object的clone()方法,就完成了对象的复制。需要注意的是,使用clone()方法的类需要实现Cloneable接口,该接口只是一个标记接口。最终的测试我们改变了克隆出来的对象的三个属性,原对象的Integer和String类型属性没有收到影响,而Father属性则收到了影响。这表明,克隆出来的对象并没有完全的复制,对于引用类型只是复制了其栈的引用。
package prototype_k;/* * @auther 顶风少年 * @mail dfsn19970313@foxmail.com * @date 2020-01-15 17:49 * @notify * @version 1.0 */ public class User implements Cloneable { private String name; private Integer age; private Father father; public User(String name, Integer age, Father father) { this.name = name; this.age = age; this.father = father; } public User copy() throws Exception { return (User) this.clone(); } public void setName(String name) { this.name = name; } public void setAge(Integer age) { this.age = age; } public void setFather(Father father) { this.father = father; } public String getName() { return name; } public Integer getAge() { return age; } public Father getFather() { return father; } }
package prototype_k;/* * @auther 顶风少年 * @mail dfsn19970313@foxmail.com * @date 2020-01-15 17:50 * @notify * @version 1.0 */ public class Father { private String name; public Father(String name) { this.name = name; } public void setName(String name) { this.name = name; } public String getName() { return name; } }
package prototype_k;/* * @auther 顶风少年 * @mail dfsn19970313@foxmail.com * @date 2020-01-15 17:49 * @notify * @version 1.0 */ public class Main { public static void main(String[] args) throws Exception { User user = new User("张三", 1, new Father("张爸爸")); User cloneUser = user.copy(); System.out.println(user == cloneUser);//false cloneUser.setName("李四"); cloneUser.setAge(2); Father father = cloneUser.getFather(); father.setName("李爸爸"); System.out.println(user.getName());//张三 System.out.println(user.getAge());//1 System.out.println(user.getFather().getName());//李爸爸 } }
深拷贝
而深度拷贝需要使用对象序列化和反序列化,这个过程叫做冷冻和解冻。很好明白的,当一个对象被读取到了流里,那么流里的对象已经不是对象了,只是二进制数据,表明完全和原有的对象切断了联系。最后解冻则是一个完完全全的新的对象。需要注意的是被序列化的对象必须实现Serializable标记接口,对象内的其他引用对象也需要实现。
package prototype_k;/* * @auther 顶风少年 * @mail dfsn19970313@foxmail.com * @date 2020-01-15 17:49 * @notify * @version 1.0 */ import java.io.*; public class User2 implements Serializable{ private String name; private Integer age; private Father father; public User2(String name, Integer age, Father father) { this.name = name; this.age = age; this.father = father; } public User2 copy() throws Exception { ByteArrayOutputStream byteInputStream = new ByteArrayOutputStream(); ObjectOutputStream outputStream = new ObjectOutputStream(byteInputStream); outputStream.writeObject(this); ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(byteInputStream.toByteArray()); ObjectInputStream inputStream = new ObjectInputStream(byteArrayInputStream); Object object = inputStream.readObject(); return (User2) object; } public void setName(String name) { this.name = name; } public void setAge(Integer age) { this.age = age; } public void setFather(Father father) { this.father = father; } public String getName() { return name; } public Integer getAge() { return age; } public Father getFather() { return father; } }
package prototype_k;/* * @auther 顶风少年 * @mail dfsn19970313@foxmail.com * @date 2020-01-15 17:50 * @notify * @version 1.0 */ import java.io.Serializable; public class Father implements Serializable { private String name; public Father(String name) { this.name = name; } public void setName(String name) { this.name = name; } public String getName() { return name; } }
package prototype_k;/* * @auther 顶风少年 * @mail dfsn19970313@foxmail.com * @date 2020-01-15 18:46 * @notify * @version 1.0 */ public class Main2 { public static void main(String[] args)throws Exception { User2 user = new User2("张三", 1, new Father("张爸爸")); User2 cloneUser = user.copy(); System.out.println(user == cloneUser); cloneUser.setName("李四"); cloneUser.setAge(2); Father father = cloneUser.getFather(); father.setName("李爸爸"); System.out.println(user.getName()); System.out.println(user.getAge()); System.out.println(user.getFather().getName()); } }