I . 原型模式 总结
1 . 原型模式本质及性能 : 原型模式使用 clone 方法克隆对象 , 其本质是在内存中拷贝二进制数据 , 这种方式要比调用 new 构造函数性能高得多 ;
2 . clone 核心是内存拷贝 : clone 对象不使用复用原有对象 , 是在内存中的另一个地址空间复制了一份一模一样的数据 , 然后将其首地址给新对象的引用 ;
3 . 原型模式适用场景 : ① 节省资源 ( 内存 CPU 硬件等 ) , ② 构造函数复杂 ( 计算繁琐 耗时 ) , ③ 创建大量对象 ;
4 . 原型模式实现 : 原型模式类实现 Cloneable 接口 , 实现其中的 clone 方法 ;
① 浅拷贝实现 : 浅拷贝默认调用 super.clone ;
② 深拷贝实现 : 深拷贝需要调用 每个引用成员对象的 clone 方法创建成员对象 , 然后赋值给新的原型模式创建的对象 , 作为其中的成员 ;
5 . 注意拷贝方式 : 默认浅拷贝 , 如果类中有引用类型成员变量 , 需要考虑深拷贝问题 , 可能会出现多个对象持有同一个引用变量 ;
II . 原型模式 浅拷贝
1 . 浅拷贝 : 调用 clone 对象拷贝内存中的数据时 , 要注意拷贝的是基础数据类型 , 对于数组 , 集合 , 自定义类等引用数据类型仅拷贝地址 , 会造成所有的对象都持有同一个内存地址的引用成员 ;
① 基础数据类型 : 如果类中全部是基础数据类型 , 使用 clone 可以将该类完整的复制一份 ;
② 引用数据类型 : 如果类中有引用类型成员 , 只是拷贝该成员的地址 , 所有的拷贝创建的原型模式实例对象都持有同一个引用 , 如果修改该引用成员的值 , 所有的原型对象实例的值都会跟着修改 ;
2 . 浅拷贝示例 :
① 原型模式类 Student : 该类中持有 Vector<String> courses 引用数据类型 , 调用 clone 方法在内存中复制对象时 , 仅复制了对象的地址 , 即将该引用的地址赋值给了 clone 出的对象 ;
package kim.hsl.design.prototype.shallowcopy; import java.util.Vector; /** * 浅拷贝示例 */ public class Student implements Cloneable { private String name; private int age; //该类在拷贝时 , 如果使用浅拷贝 , 只是将地址拷贝走了 , 两个对象实际上用的是同一个对象 private Vector<String> courses = new Vector<>(); public Student() { System.out.println("调用 Student 默认构造函数"); } public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } public Vector<String> getCourses() { return courses; } public void setCourses(Vector<String> courses) { this.courses = courses; } @Override public String toString() { return "Student{" + "name='" + name + '\'' + ", age=" + age + ", courses=" + courses + '}'; } @Override protected Object clone() throws CloneNotSupportedException { System.out.println("调用 Student clone 方法"); return super.clone(); } }
② 测试类 Main : 此处创建了两个 Student 实例对象 , 但是两个对象都持有同一个 Vector<String> courses 引用数据类型成员 , 当修改其中一个成员时 , 两个对象中的该成员都会改变 ;
package kim.hsl.design.prototype.shallowcopy; public class Main { public static void main(String[] args) { try { //测试使用 clone 方法实现的原型模式 , 使用原型模式创建 2 个对象 Student newStudent = new Student(); // 1 . 使用 clone 方法创建对象1 Student student = (Student) newStudent.clone(); student.setName("Tom"); student.setAge(10); student.getCourses().add("数学"); // 2 . 使用 clone 方法创建对象2 Student student2 = (Student) newStudent.clone(); student2.setName("Jerry"); student2.setAge(18); student2.getCourses().add("语文"); System.out.println("student : " + student + "\nstudent2 : " + student2); } catch (CloneNotSupportedException e) { //捕获 clone 方法可能产生的异常 e.printStackTrace(); } } }
③ 执行结果 : 调用第一个对象 add 方法 , 在 vector 集合中添加了 “数学” 字符串 , 调用第二个对象的 add 方法 , 向 courses 集合中添加 “语文” 字符串 , 理论上两个分别是 “数学” 和 “语文” , 但是此处却都变成了 “数学” “语文” 两个课程 , 说明两个原型模式对象持有的 Vector<String> courses 变量是指向同一个内存地址的 ;
调用 Student 默认构造函数 调用 Student clone 方法 调用 Student clone 方法 student : Student{name='Tom', age=10, courses=[数学, 语文]} student2 : Student{name='Jerry', age=18, courses=[数学, 语文]}
III . 原型模式 深拷贝
1 . 深拷贝策略 : 深拷贝时需要在 clone 方法中 , 调用引用数据类型本身的 clone 对象 , 在将其赋值给被拷贝的原型模式实例对象 ;
2 . 深拷贝 clone 方法流程 :
① 创建实例对象 : 通过 clone 方法 , 创建原型模式类的实例对象 , 此时该对象的引用成员处于浅拷贝状态 ;
② 拷贝引用成员 : 调用原型模式类对象成员的 clone 对象 , 创建新的成员对象 , 将新的成员对象赋值给克隆出的原型模式对象 ;
③ 返回新的对象 : 返回 clone 创建的原型模式实例对象 ;