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

简介: 浅克隆和深克隆的详细教程~

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

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

package originModel;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@NoArgsConstructor
@AllArgsConstructor
class Teacher implements Cloneable{
    private String name;
    private Student student;
}
package originModel;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Student {
    private int age;
    private String name;
}
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);
    }
}

输出如下所示:

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

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

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

浅克隆:

实现步骤:

原型对象实现
1:实现Cloneable接口
2:实现clone方法
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();
    }

测试类:

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

输出如下所示:

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

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

验证如下所示:

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

输出如下所示:

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

Student(age=19, name=小明)
Student(age=19, name=小明)
true
Student(age=20, name=李华)
Student(age=20, name=李华)
true
张三
李红
false

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


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


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


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

深克隆:

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

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

如下所示:

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

既然要调用引用对象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();
    }
}

修改测试类如下所示:

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

输出如下所示:

Student(age=19, name=小明)
Student(age=19, name=小明)
false
Student(age=19, name=小明)
Student(age=20, name=李华)
false
张三
张三
true
张三
李红
false

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

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

第一步导入对应的依赖:

<dependency>
            <groupId>com.google.code.gson</groupId>
            <artifactId>gson</artifactId>
            <version>2.10.1</version>
</dependency>

如下所示:

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

输出如下所示:

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

Student(age=19, name=小明)
Student(age=19, name=小明)
false
Student(age=19, name=小明)
Student(age=20, name=李华)
false
张三
张三
false
张三
李红
false
相关文章
|
7月前
|
JSON JavaScript 前端开发
怎么做深拷贝?
怎么做深拷贝?
|
7月前
原型模式——克隆
原型模式——克隆
46 0
|
3月前
|
存储 JSON 前端开发
栈在前端中的应用,顺便再了解下深拷贝和浅拷贝!
该文章探讨了栈在前端开发中的应用,并深入讲解了JavaScript中深拷贝与浅拷贝的区别及其实现方法。
栈在前端中的应用,顺便再了解下深拷贝和浅拷贝!
|
设计模式 Java 程序员
谈谈你对深克隆和浅克隆的理解
一个工作了5年的程序员私信我说,前几天去阿里面试被问到这样一个这样的面试题,说谈谈你对深克隆和浅克隆的理解。他回答说深克隆是克隆值,浅克隆是克隆引用,当时他只说了这样一句话,回答完以后,他看到面试官的表情很诧异,面试也没有继续深入追问了。小伙伴们,如果是你来回答,你也会这样回回答吗?
85 0
|
存储
【克隆方法+深浅拷贝】
【克隆方法+深浅拷贝】
70 0
|
设计模式 Java API
一文读懂深克隆与浅克隆的关系
在Java提供的API中,不需要手动创建抽象原型接口,因为Java已经内置了Cloneable抽象原型接口,自定义的类型只需实现该接口并重写Object.clone()方法即可完成本类的复制。 通过查看JDK的源码可以发现,其实Cloneable是一个空接口。Java之所以提供Cloneable接口,只是为了在运行时通知Java虚拟机可以安全地在该类上使用clone()方法。而如果该类没有实现 Cloneable接口,则调用clone()方法会抛出 CloneNotSupportedException异常。 一般情况下,如果使用clone()方法,则需满足以下条件。
74 0
|
存储 Java fastjson
简单理解浅克隆和深克隆
简单理解浅克隆和深克隆
239 0
简单理解浅克隆和深克隆
|
JavaScript
原生js实现对象的克隆
原生js实现对象的克隆
原生js实现对象的克隆
|
JSON JavaScript 数据格式
细说浅拷贝与深拷贝
js的浅拷贝与深拷贝在业务中时常有用到,关于浅拷贝与深拷贝的剖析文章层出不穷,本文是笔者对于深拷贝与浅拷贝的理解,一起来夯实js语言基础知识的理解吧。
131 0
细说浅拷贝与深拷贝
|
存储 JSON Java
面试题:图解深克隆和浅克隆
获得好工作的秘密:有效的准备面试。
207 0
面试题:图解深克隆和浅克隆
下一篇
DataWorks