原型(prototype)模式:用一个已经创建好的实例作为原型,通过复制该原型对象来创建一个和原型相同或者相似的对象

优点:

  1. java自带的原型模式基于内存二进制流的复制,在性能上要比new对象要快的多
  2. 可以使用深拷贝的方式保存对象的状态,使用原型模式复制对象并将之保存起来,简化了创建对象的过程

缺点:

  1. 需要为每一个类都配备一个clone方法
  2. clone方法位于代码的内部,当对已有的类进行修改的时候,需要修改代码,违背了开闭原则
  3. 当深拷贝的是嵌套对象的时候,代码实现相对复杂

适用场景

  1. 对象之间相同或相似,即只是个别的几个属性不同的时候。
  2. 创建对象成本较大,例如初始化时间长,占用CPU太多,或者占用网络资源太多等,需要优化资源。
  3. 创建一个对象需要繁琐的数据准备或访问权限等,需要提高性能或者提高安全性。
  4. 系统中大量使用该类对象,且各个调用者都需要给它的属性重新赋值。

原型模式的结构:

  1. 抽象原型类:规定了具体原型对象必须实现的接口
  2. 具体原型类:实现了抽象原型类的clone方法,他是可复制的对象
  3. 访问类:使用具体原型类中的clone方法来复制新的对象

分类:

  1. 浅拷贝:新对象中的属性和原对象完全相同,非基本类型的属性指向原属性所在内存地址。java中Object类中的clone方法就已经实现了浅拷贝
  2. 深拷贝:新对象中的属性和非基本属性都会被拷贝,不再指向原内存地址

浅拷贝

看结果发现,浅拷贝没有拷贝引用类型的属性

  1. public class Prototype {
  2. public static void main(String[] args) throws CloneNotSupportedException {
  3. // 定义学生1,和老师1
  4. Student s1 = new Student();
  5. Teacher t1 = new Teacher();
  6. t1.setName("张三的老师");
  7. s1.setTeacher(t1);
  8. s1.setName("张三");
  9. // 拷贝学生1
  10. Student s2 = s1.clone();
  11. s2.setName("李四");
  12. s2.getTeacher().setName("李四的老师");
  13. System.out.println("学生"+s1.name+"的老师是:"+s1.getTeacher().getName()); // 学生张三的老师是:李四的老师
  14. System.out.println("学生"+s2.name+"的老师是:"+s2.getTeacher().getName()); // 学生李四的老师是:李四的老师
  15. }
  16. static class Student implements Cloneable{
  17. // 学生姓名
  18. String name;
  19. Teacher teacher;
  20. // 省略get set
  21. /**
  22. * 重写clone 对学生类进行拷贝
  23. */
  24. @Override
  25. protected Student clone() throws CloneNotSupportedException {
  26. return (Student)super.clone();
  27. }
  28. }
  29. static class Teacher implements Cloneable{
  30. // 老师姓名
  31. String name;
  32. // 省略get set
  33. @Override
  34. protected Teacher clone() throws CloneNotSupportedException {
  35. return (Teacher) super.clone();
  36. }
  37. }
  38. }

深拷贝

对上述代码进行改造,只需要重写学生类中的拷贝方法,实现深拷贝

/**
 * 重写clone 对学生类进行拷贝
 */
@Override
protected Student clone() throws CloneNotSupportedException {
    Student student = (Student) super.clone();
    Teacher t1 = this.teacher.clone();
    student.setTeacher(t1);
    return student;
}