• 浅克隆和深克隆的详细教程~


    原型模式用于创建重复的对象,同时又能保证性能。这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。

    传统创建重复对象的方法:

    package originModel;
    
    
    import lombok.AllArgsConstructor;
    import lombok.Data;
    import lombok.NoArgsConstructor;
    
    @Data
    @NoArgsConstructor
    @AllArgsConstructor
    class Teacher implements Cloneable{
        private String name;
        private Student student;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    package originModel;
    
    
    import lombok.AllArgsConstructor;
    import lombok.Data;
    import lombok.NoArgsConstructor;
    
    @Data
    @NoArgsConstructor
    @AllArgsConstructor
    public class Student {
        private int age;
        private String name;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    package originModel;
    
    public class test1 {
        public static void main(String[] args) {
            Teacher teacher1=new Teacher();
            teacher1.setName("张三");
            teacher1.setStudent(new Student(19,"小明"));
            Teacher teacher2=new Teacher(teacher1.getName(),teacher1.getStudent());
            System.out.println(teacher1);
            System.out.println(teacher2);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    输出如下所示:

    Teacher(name=张三, student=Student(age=19, name=小明))
    Teacher(name=张三, student=Student(age=19, name=小明))
    
    • 1
    • 2

    传统方式虽然比较好理解而且也容易操作,但是在创建新的对象时,总是需要获取原始对象的属性,如果创建的对象比较复杂,这种方式效率很低。

    通过原型模式创建重复对象的方法:本质“克隆”

    浅克隆:

    实现步骤:

    原型对象实现
    1:实现Cloneable接口
    2:实现clone方法
    
    • 1
    • 2
    • 3
    package originModel;
    
    import lombok.AllArgsConstructor;
    import lombok.Data;
    import lombok.NoArgsConstructor;
    
    @Data
    @NoArgsConstructor
    @AllArgsConstructor
    class Teacher implements Cloneable {
        private String name;
        //student类为Teacher类的成员变量
        private Student student;
        @Override
        public Object clone() throws CloneNotSupportedException {
            //浅克隆
            return super.clone();
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    测试类:

    package originModel;
    
    public class test1 {
        public static void main(String[] args) throws CloneNotSupportedException {
            Teacher teacher1=new Teacher();
            teacher1.setName("张三");
            teacher1.setStudent(new Student(19,"小明"));
            Teacher teacher2= (Teacher) teacher1.clone();
            System.out.println(teacher1);
            System.out.println(teacher2);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    输出如下所示:

    Teacher(name=张三, student=Student(age=19, name=小明))
    Teacher(name=张三, student=Student(age=19, name=小明))
    
    • 1
    • 2

    通过克隆我们得到了和原型对象值完全相同的克隆对象,如果我们修改克隆对象的值是否会影响到原型对象呢?

    验证如下所示:

    package originModel;
    public class test1 {
        public static void main(String[] args) throws CloneNotSupportedException {
            Teacher teacher1=new Teacher();
            teacher1.setName("张三");
            teacher1.setStudent(new Student(19,"小明"));
            Teacher teacher2= (Teacher) teacher1.clone();
            System.out.println(teacher1.getStudent());
            System.out.println(teacher2.getStudent());
            System.out.println(teacher1.getStudent()==teacher2.getStudent());
            // 验证1:克隆对象中引用数据类型值的改变是否会影响到原型对象?
            teacher2.getStudent().setName("李华");
            teacher2.getStudent().setAge(20);
            System.out.println(teacher1.getStudent());
            System.out.println(teacher2.getStudent());
            System.out.println(teacher1.getStudent()==teacher2.getStudent());
            // 验证2:克隆对象中基本数据类型值的改变是否会影响到原型对象?
            teacher2.setName("李红");
            System.out.println(teacher1.getName());
            System.out.println(teacher2.getName());
            System.out.println(teacher1.getName()==teacher2.getName());
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23

    输出如下所示:

    修改克隆对象中引用数据类型的值影响到了原型对象,而修改克隆对象中基本数据类型的值并没有影响到原型对象

    Student(age=19, name=小明)
    Student(age=19, name=小明)
    true
    Student(age=20, name=李华)
    Student(age=20, name=李华)
    true
    张三
    李红
    false
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    出现上述结果的原因如下所示:

    克隆对象中的基本数据类型对象是直接存储在克隆对象自身的内存空间中而不是引用原型对象的内存空间当修改克隆对象中的基本数据类型对象的值时,只是修改了克隆对象自身的值,不会影响原型对象的值。

    克隆对象中的引用数据类型对象则是引用了原型对象的内存地址,它们指向同一个对象当修改克隆对象中的引用数据类型对象的值时,实际上是修改了原型对象的值,因为它们共享同一块内存空间。所以修改克隆对象中引用数据类型对象的值会导致原型对象的值发生改变。

    如果我们不希望对克隆对象的修改影响到原始对象,那么我们可以使用深克隆实现:

    深克隆:

    是指创建一个新的对象这个新对象与原对象是完全独立的它们在内存中的地址是不同的。因此,修改克隆对象中引用类型的值不会影响原型对象。

    方式 1:重写clone方法,依次调用成员变量的clone方法
    
    • 1

    如下所示:

    package originModel;
    import lombok.AllArgsConstructor;
    import lombok.Data;
    import lombok.NoArgsConstructor;
    @Data
    @NoArgsConstructor
    @AllArgsConstructor
    class Teacher implements Cloneable{
        private String name;
        private Student student;
    
        @Override
        protected Object clone() throws CloneNotSupportedException {
            Teacher teacher= (Teacher) super.clone();//该行实现的依然是浅克隆
            //克隆引用对象
            Student student1= (Student) teacher.getStudent().clone();
            teacher.setStudent(student1);
            return teacher;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    既然要调用引用对象Student的克隆方法,那么Student类也要实现对应的Cloneable接口和clone方法

    package originModel;
    
    
    import lombok.AllArgsConstructor;
    import lombok.Data;
    import lombok.NoArgsConstructor;
    
    @Data
    @NoArgsConstructor
    @AllArgsConstructor
    public class Student implements Cloneable{
        private int age;
        private String name;
    
        @Override
        protected Object clone() throws CloneNotSupportedException {
            return super.clone();
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    修改测试类如下所示:

    package originModel;
    public class test1 {
        public static void main(String[] args) throws CloneNotSupportedException {
            Teacher teacher1=new Teacher();
            teacher1.setName("张三");
            teacher1.setStudent(new Student(19,"小明"));
            Teacher teacher2= (Teacher) teacher1.clone();
            System.out.println(teacher1.getStudent());
            System.out.println(teacher2.getStudent());
            System.out.println(teacher1.getStudent()==teacher2.getStudent());
            // 验证1:克隆对象中引用数据类型值的改变是否会影响到原型对象?
            teacher2.getStudent().setName("李华");
            teacher2.getStudent().setAge(20);
            System.out.println(teacher1.getStudent());
            System.out.println(teacher2.getStudent());
            System.out.println(teacher1.getStudent()==teacher2.getStudent());
            // 验证2:克隆对象中基本数据类型值的改变是否会影响到原型对象?
            System.out.println(teacher1.getName());
            System.out.println(teacher2.getName());
            System.out.println(teacher1.getName()==teacher2.getName());
            teacher2.setName("李红");
            System.out.println(teacher1.getName());
            System.out.println(teacher2.getName());
            System.out.println(teacher1.getName()==teacher2.getName());
        }
    }
    
    • 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

    输出如下所示:

    Student(age=19, name=小明)
    Student(age=19, name=小明)
    false
    Student(age=19, name=小明)
    Student(age=20, name=李华)
    false
    张三
    张三
    true
    张三
    李红
    false
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    但是如果一个类中包含的引用类型对象有很多,我们使用这种方式的效率就非常低

    方式2:使用Json工具,比如GSON对象序列化和反序列化实现深克隆
    
    • 1

    第一步导入对应的依赖:

    <dependency>
                <groupId>com.google.code.gsongroupId>
                <artifactId>gsonartifactId>
                <version>2.10.1version>
    dependency>
    
    • 1
    • 2
    • 3
    • 4
    • 5

    如下所示:

    package originModel;
    
    import com.google.gson.Gson;
    
    public class test1 {
        public static void main(String[] args) throws CloneNotSupportedException {
            Teacher teacher1=new Teacher();
            teacher1.setName("张三");
            teacher1.setStudent(new Student(19,"小明"));
            Gson gson=new Gson();
            //对象的序列化
            String json=gson.toJson(teacher1);
            //对象的反序列化
            Teacher teacher2=gson.fromJson(json,Teacher.class);
            System.out.println(teacher1.getStudent());
            System.out.println(teacher2.getStudent());
            System.out.println(teacher1.getStudent()==teacher2.getStudent());
            // 验证1:克隆对象中引用数据类型值的改变是否会影响到原型对象?
            teacher2.getStudent().setName("李华");
            teacher2.getStudent().setAge(20);
            System.out.println(teacher1.getStudent());
            System.out.println(teacher2.getStudent());
            System.out.println(teacher1.getStudent()==teacher2.getStudent());
            // 验证2:克隆对象中基本数据类型值的改变是否会影响到原型对象?
            System.out.println(teacher1.getName());
            System.out.println(teacher2.getName());
            System.out.println(teacher1.getName()==teacher2.getName());
            teacher2.setName("李红");
            System.out.println(teacher1.getName());
            System.out.println(teacher2.getName());
            System.out.println(teacher1.getName()==teacher2.getName());
        }
    }
    
    • 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

    输出如下所示:

    对克隆对象(teacher2)的修改没有影响到原始对象(teacher1)

    Student(age=19, name=小明)
    Student(age=19, name=小明)
    false
    Student(age=19, name=小明)
    Student(age=20, name=李华)
    false
    张三
    张三
    false
    张三
    李红
    false
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
  • 相关阅读:
    第五届“传智杯”全国大学生计算机大赛(练习赛) [传智杯 #5 练习赛] 复读
    机器学习笔记 - 模式识别的应用场景之一简单车牌识别
    容器适配器【stack、queue、priority_queue和反向迭代器】
    基于单片机设计的电子指南针(LSM303DLH模块(三轴磁场 + 三轴加速度)
    Golang 切片删除指定元素的几种方法
    SpringBoot中配置、启动Tomcat总结
    springBoot 指定自动扫描路劲
    javaweb JDBC练习
    java基于springboot电动车实名挂牌系统
    算法训练 第八周
  • 原文地址:https://blog.csdn.net/m0_64365419/article/details/134068822