原型设计模式(创建型模式)
用原型实例指定创建对象的种类,并且通过拷贝这些原型,创建新的对象,且无需知道如何创建的细节。
适用于创建复杂的或者构建耗时的对象,通过原型拷贝效率高效。
如图所示,浅拷贝案例,实现 Cloneable接口,重写Object类下的clone()方法完成克隆。
创建对象花费2000多毫秒,克隆几乎没有花费时间,说明克隆不走构造函数。
克隆的对象 hashCode 与 原来的对象不一样,说明产生了一个新的对象,并且把原有的属性值也赋值给克隆对象。(赋值实际是将引用地址赋值)
这里就谈谈 浅拷贝 深拷贝
浅拷贝
需要拷贝了类,必需实现Cloneable接口,因为Object.clone()是protected,所以需要重写,才能调用。
会在内存中新开辟一个空间,存放拷贝后的对象,且属性会继续引用拷贝前的内存地址。
在 User 类中新加一个 Employee 引用属性
浅拷贝,只是重新建立了一个对象,但之前的引用属性内存地址都没有发生变化,修改克隆类里的 Employee里的属性,之前的对象也会发生改变。
但是修改 String 类型的数据,之前的对象不会发生改变,因为String类有 final 修饰符;
深拷贝
为所有引用数据类型的成员变量申请存储空间,并复制每个引用数据类型成员变量所引用的对象,直到该对象可达的所有对象。也就是说,对象进行深拷贝要对整个对象进行拷贝
Employee 也实现 Cloneable接口,重写 clone() 方法
User 类的 clone() 对User拷贝后,再设置 employee引用对象也调用克隆方法
运行结果,引用对象的 employee就是一个新的对象,新的内存地址。
以上是 重写clone方法来实现深拷贝,就是需要拷贝的对象,所有的引用对象属性的类,都需要重写克隆方法,且拷贝对象的引用对象的属性都需要调用克隆方法赋值,这样的方式缺点,如果引用对象属性很多,则都需要调用clone方法赋值,很是繁琐。
通过对象序列化实现深拷贝 类必需实现 Serializable 接口,不然序列化会报错
public class Test {
public static void main(String[] args) throws Exception {
User user = new User("张三", "男");
System.out.println("拷贝前:user.employee " + user.getEmployee());
ByteArrayOutputStream baos = null;
ObjectOutputStream oos = null;
ByteArrayInputStream bais = null;
ObjectInputStream ois = null;
try {
baos = new ByteArrayOutputStream();
oos = new ObjectOutputStream(baos);
/* 写入 user 对象 */
oos.writeObject(user);
bais = new ByteArrayInputStream(baos.toByteArray());
ois = new ObjectInputStream(bais);
/* 读取 user 对象 */
User clone = (User) ois.readObject();
clone.getEmployee().setNickname("经理"); // 为拷贝后的对象的 Employee.nickname 重新赋值
clone.setName("李四"); // 为拷贝后的对象的 name 重新赋值
System.out.println("user " + user);
System.out.println("user.employee.hashCode " + user.getEmployee().hashCode());
System.out.println("user.employee " + user.getEmployee());
System.out.println("clone " + clone);
System.out.println("clone.employee.hashCode " + clone.getEmployee().hashCode());
System.out.println("clone.employee " + clone.getEmployee());
} catch (Exception e) {
e.printStackTrace();
} finally {
if (baos != null) {
baos.close();
}
if (oos != null) {
oos.close();
}
if (bais != null) {
bais.close();
}
if (ois != null) {
ois.close();
}
}
}
}
运行结果;产生的是新对象,引用对象属性也是新的对象;
通过对象序列化实现深拷贝,可以减少频繁的调用 clone 方法进行克隆,减小了开发成本。
觉得对您有帮助,就点个赞呗。😀