【设计模式】原型模式 ( 浅拷贝 | 深拷贝 | 原型与单例冲突 | 禁用 final )

举报
韩曙亮 发表于 2022/01/11 02:12:56 2022/01/11
【摘要】 文章目录 I . 原型模式 总结II . 原型模式 浅拷贝III . 原型模式 深拷贝IV . 原型模式 与 单例V . 原型模式 中的 final 关键字 ( 禁止出现 ) ...



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();
    }
}

  
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54

② 测试类 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();
        }
    }
}

  
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28

③ 执行结果 : 调用第一个对象 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=[数学, 语文]}

  
 
  • 1
  • 2
  • 3
  • 4
  • 5


III . 原型模式 深拷贝



1 . 深拷贝策略 : 深拷贝时需要在 clone 方法中 , 调用引用数据类型本身的 clone 对象 , 在将其赋值给被拷贝的原型模式实例对象 ;


2 . 深拷贝 clone 方法流程 :


① 创建实例对象 : 通过 clone 方法 , 创建原型模式类的实例对象 , 此时该对象的引用成员处于浅拷贝状态 ;

② 拷贝引用成员 : 调用原型模式类对象成员的 clone 对象 , 创建新的成员对象 , 将新的成员对象赋值给克隆出的原型模式对象 ;

③ 返回新的对象 : 返回 clone 创建的原型模式实例对象 ;


3 . 示例代码 :


① 原型模式深拷贝示例 : 深拷贝与浅拷贝只是在 clone 方法中表现不同 , 其它代码一致 ; 在 clone 方法中需要针对引用类型进行克隆 ;

package kim.hsl.design.prototype.deepcopy;

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> course) {
        this.courses = course;
    }

    @Override
    public String toString() {
        return "Student{" +
                "name='" + name + '\'' +
                ", age=" + age +
                ", course=" + courses +
                '}';
    }

    @Override
    protected Object clone() throws CloneNotSupportedException {
        System.out.println("调用 Student clone 方法");

        //1 . 首先拷贝一个基本对象
        Student student = (Student) super.clone();

        //2 . 将引用类型的对象单独克隆赋值
        student.courses = (Vector<String>) this.courses.clone();

        //3 . 返回创建的新的对象
        return student;
    }
}

  
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63

② 测试代码 :

package kim.hsl.design.prototype.deepcopy;

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();
        }
    }
}

  
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28

③ 运行结果 : 原型模式的两个实例对象的互不干扰 ;


调用 Student 默认构造函数
调用 Student clone 方法
调用 Student clone 方法
student : Student{name='Tom', age=10, course=[数学]}
student2 : Student{name='Jerry', age=18, course=[语文]}

  
 
  • 1
  • 2
  • 3
  • 4
  • 5


IV . 原型模式 与 单例



1 . 原型模式 与 单例模式 :


① 原型模式 : 原型模式的核心是不调用构造函数 , 使用 clone 方法在内存中克隆对象 ;

② 单例模式 : 单例模式的核心是私有化构造函数 , 控制外部用户 , 不能随意调用构造函数创建对象 ;


2 . Cloneable 破坏了单例模式 : 此处二者就出现了矛盾 , 如果单例类 , 实现了 Cloneable 接口 , 那么该类就可以调用 clone 方法创建另外的实例对象 , 因此破坏了单例模式 ;


3 . 解决方案 :

① 不实现 Cloneable 接口 : 单例模式中不要实现 Cloneable 接口 , 不提供内存拷贝功能 ;

② clone 中调用单例 : 如果必须实现 Cloneable 接口 , 那么在重写的 clone 方法中 , 调用获取单例类的方法 , 不要进行内存对象拷贝创建新的实例对象 ;



V . 原型模式 中的 final 关键字 ( 禁止出现 )



1 . final 作用 : final 用于修饰常量 , 被 final 修饰的变量无法重新赋值 ;


2 . Cloneable 实现类成员不要使用 final : 在原型模式的类中 , 实现了 Cloneable 接口 , 重写了 clone 方法 , 其类对象的成员不能被 final 修饰 , 否则无法重新赋值 ;

文章来源: hanshuliang.blog.csdn.net,作者:韩曙亮,版权归原作者所有,如需转载,请联系作者。

原文链接:hanshuliang.blog.csdn.net/article/details/105318165

【版权声明】本文为华为云社区用户转载文章,如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。