现有一只羊tom,姓名为tom, 年龄为1,颜色为白色,请编写程序创建和tom羊属性完全相同的2只羊
1.传统方法
1.1 代码实现
public class Client {
public static void main(String[] args) {
Sheep sheep = new Sheep("tom", 1, "白色");
Sheep sheep2 = new Sheep(sheep.getName(), sheep.getAge(), sheep.getColor());
Sheep sheep3 = new Sheep(sheep.getName(), sheep.getAge(), sheep.getColor());
}
}
1.2 优缺点
- 优点:
- 好理解,易操作
- 缺点:
- 效率较低
- 需要重新初始化对象,而不是动态地获得对象运行时的状态, 不够灵活
2.原型模式
2.1 概念
-
指用原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象
-
创建型设计模式,允许一个对象再创建另外一个可定制的对象,无需知道如何创建的细节
-
通过将原型对象传给那个要发动创建的对象,该发动创建的对象通过请求原型对象拷贝它们自己来实施创建,即
对象.clone()
2.2 优缺点
- 优点:
- 创建新的对象比较复杂时,可利用原型模式简化对象的创建过程
- 不用重新初始化对象,动态地获得对象运行时的状态
- 如果原始对象发生变化(比如增加或减少属性),其它克隆对象的也会发生相应的变化,无需修改代码
- 缺点:
- 在实现深克隆的时候可能需要比较复杂的代码
- 需要为每一个类配备一个克隆方法(意味着对已有的类进行改造时需要修改其源代码,违背了OCP原则)
2.3 代码实现
public class Sheep implements Cloneable {
...
@Override
protected Object clone(){
Sheep sheep = null;
try {
sheep = (Sheep) super.clone();
} catch (CloneNotSupportedException e) {
System.out.println(e.getMessage());
}
return sheep;
}
}
public class Client {
public static void main(String[] args) {
Sheep sheep = new Sheep("tom", 1, "白色");
// 此时如果在Sheep类中增加一个属性(赋了默认值)
// 如果使用传统方法则需要写为:
// Sheep sheep2 =
// new Sheep(sheep.getName(), sheep.getAge(), sheep.getColor(),sheep.getAddress());
Sheep sheep2 = (Sheep) sheep.clone();
Sheep sheep3 = (Sheep) sheep.clone();
}
}
2.3 应用场景
Spring中原型bean的创建,就是原型模式的应用
// beans.xml
<bean id="emp" class="com.psj.emp" scope="prototype"/>
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("beans.xml");
Object bean = applicationContext.getBean("emp");
3.浅拷贝和深拷贝
3.1 浅拷贝
-
对于基本数据类型的成员变量,浅拷贝会直接进行值传递(即将该属性值复制一份给新的对象)
-
对于引用数据类型的成员变量,浅拷贝会进行引用传递(即只将该成员变量的内存地址复制给新对象)。一个对象修改该成员变量会影响到另一个对象的该成员变量值
-
浅拷贝是使用默认的clone()方法来实现(2中的拷贝就是浅拷贝)
3.2 深拷贝
-
复制对象的所有基本数据类型的成员变量值
-
为引用数据类型的成员变量申请存储空间,并复制每个引用数据类型成员变量所引用的对象,直到该对象可达的所有对象
-
实现方式:
- 重写clone方法:对引用数据类型单独调用clone方法
public class DeepProtoType implements Serializable, Cloneable { public String name; // String属性 public DeepCloneableTarget deepCloneableTarget; // 引用类型(该类中的属性都是基本数据类型了) public DeepProtoType() {} // 重写clone 方法 @Override protected Object clone() throws CloneNotSupportedException { Object deep = null; // 完成对基本数据类型和String的克隆 deep = super.clone(); // 对引用类型的属性进行单独处理 DeepProtoType deepProtoType = (DeepProtoType) deep; deepProtoType.deepCloneableTarget = (DeepCloneableTarget) deepCloneableTarget.clone(); return deepProtoType; } }
- 对象序列化(推荐):
public class DeepProtoType implements Serializable, Cloneable { public String name; // String属性 public DeepCloneableTarget deepCloneableTarget; // 引用类型 public DeepProtoType() { } // 通过对象的序列化实现 public Object deepClone() { // 创建流对象 ByteArrayOutputStream bos = null; ObjectOutputStream oos = null; ByteArrayInputStream bis = null; ObjectInputStream ois = null; try { // 序列化 bos = new ByteArrayOutputStream(); oos = new ObjectOutputStream(bos); oos.writeObject(this); // 当前这个对象以对象流的方式输出 // 反序列化 bis = new ByteArrayInputStream(bos.toByteArray()); ois = new ObjectInputStream(bis); DeepProtoType copyObj = (DeepProtoType) ois.readObject(); return copyObj; } catch (Exception e) { e.printStackTrace(); return null; } finally { // 关闭流 try { bos.close(); oos.close(); bis.close(); ois.close(); } catch (Exception e2) { System.out.println(e2.getMessage()); } } } }
public class Client { public static void main(String[] args) throws Exception { DeepProtoType p = new DeepProtoType(); p.name = "psj"; p.deepCloneableTarget = new DeepCloneableTarget("psj1", "psj2"); // 重写clone方法 DeepProtoType p2 = (DeepProtoType) p.clone(); // 两个hashcode不一样 System.out.println(p.deepCloneableTarget.hashCode()); System.out.println(p2.deepCloneableTarget.hashCode()); // 对象序列化 DeepProtoType p3 = (DeepProtoType) p.deepClone(); // 两个hashcode不一样 System.out.println(p.deepCloneableTarget.hashCode()); System.out.println(p3.deepCloneableTarget.hashCode()); } }
参考
https://www.bilibili.com/video/BV1G4411c7N4?p=49-54