首页 > 其他分享 >讲讲深拷贝浅拷贝

讲讲深拷贝浅拷贝

时间:2024-08-23 20:53:39浏览次数:5  
标签:category 讲讲 对象 clone a1 person Animal 拷贝

什么是值传递和引用传递?

  • 值传递是对基本型变量而言的,传递的是该变量的一个副本,改变副本不影响原变量。
  • 引用传递一般是对于对象型变量而言的,传递的是该对象地址的一个副本,并不是原对象本身,两者指向同一片内存空间。所以对引用对象进行操作会同时改变原对象。

java中不存在引用传递,只有值传递。即不存在变量a指向变量b,变量b指向对象的这种情况。

clone()

clone方法是Java中拷贝对象的方法

在Java中拷贝对象其实是指复制一个与原对象一样的新对象出来,但是在Java中 赋值 = 是复制对象引用,如果我们想要得到一个对象的副本,使用赋值操作是无法达到目的的。

看下面例子:

Animal a1 = new Animal();
a1.category = "人类";
Animal a2 = a1;
System.out.println(a1==a2);//true
a2.category = "猫科动物";
System.out.println(a1.category);//猫科动物

显然,a1==a2返回为true,a1和a2指向的是同一个引用

并且对对象a2的属性值进行修改后,a1的属性值也跟着改变,因此这不是拷贝,要实现拷贝,需要用到Object对象的clone()方法,实现对对象中各个属性的复制,但它的可见范围是protected的

protected native Object clone() throws CloneNotSupportedException;
  • 所以实体类使用clone()方法的前提是:实现Cloneable接口,这是一个标记接口,自身没有方法,这算是一种约定。调用clone方法时,会去判断有没有实现Cloneable接口,没有实现Cloneable的话会抛异常CloneNotSupportedException。

  • 覆盖clone()方法,可见性提升为public。

public class Animal implements Cloneable {
    String category;

    @Override
    protected Object clone() throws CloneNotSupportedException {
        return super.clone();
    }

    public static void main(String[] args) throws CloneNotSupportedException {
        Animal a1 = new Animal();
        a1.category = "人类";
        Animal a2 = (Animal) a1.clone();
        System.out.println(a1 == a2);
        a2.category = "猫科动物";

        System.out.println(a1.category);
    }
}
//输出:
false
人类

拷贝了一个新对象,与原对象引用不同,因此返回false,并且修改新对象的属性值,旧对象的属性值不改变

浅拷贝

当类中含有引用类型的属性时,新对象和旧对象的引⽤类型属性指向的是同⼀个对象,即为浅拷贝

public class Animal implements Cloneable {
    String category;
    Person person;

    @Override
    protected Object clone() throws CloneNotSupportedException {
        return super.clone();
    }

    public static void main(String[] args) throws CloneNotSupportedException {
        Animal a1 = new Animal();
        Person person = new Person("旧对象");
        a1.category = "人类";
        a1.person = person;

        Animal a2 = (Animal) a1.clone();
        a2.category = "新人类";
        a2.person.name = "新对象";

        System.out.println(a1.category + ":" + a1.person.name);//人类:新对象
    }
}

如上,改变 a2中的引用类型 person.name = "新对象"后,a1的的引用类型 person.name 也发生了改变。

也就是说,新对象和旧对象的引⽤类型属性指向的是同⼀个对象,因此当改变新对象的引用类型(person)的属性值时,旧对象的引用类型的属性值也被改变。因此只是浅拷贝

这里可能就有疑问了,String类型也是引用类型,为什么进行了深拷贝,即如上所示,改变a2的属性值category,但却没有改变a1的category?
String类型有点特殊,它本身没有实现Cloneable接口,故根本无法克隆,只能传递引用(注意:Java只有值传递,只是这里传递是原来引用地址值)。在clone()后,克隆后的对象开始也是指向的原来引用地址值(刚克隆完检查a1.category == a2.category 为true),但是一旦String的值发生改变(String作为不可更改的类——immutable class,在新赋值的时候,会创建了一个新的对象)就改变了克隆后对象指向的地址,让它指向了一个新的String地址,不会影响原对象的指向和值,原来的String对象还是指向的它自己的的地址。这样String在拷贝的时候就表现出了深拷贝的特点

深拷贝

当类中含有引用类型的属性时,新对象和旧对象的引⽤类型属性指向的不是同⼀个对象,即为深拷贝

要实现深拷贝,在clone方法中不仅调用了super.clone,而且需要调用Person对象的clone方法(Person也要实现Cloneable接口并重写clone方法),从而实现了深拷贝。

public class Animal implements Cloneable {
    String category;
    Person person;

    @Override
    protected Object clone() throws CloneNotSupportedException {
        Animal animal = null;
        animal = (Animal) super.clone();
        animal.person = (Person) person.clone();
        return animal;
    }

    public static void main(String[] args) throws CloneNotSupportedException {
        Animal a1 = new Animal();
        Person person = new Person("旧对象");
        a1.category = "人类";
        a1.person = person;

        Animal a2 = (Animal) a1.clone();
        a2.person.name = "新对象";

        System.out.println(a1.person.name);//旧对象
    }
}

可以看到,新对象的引用类型 person 不会再受到旧对象的影响。

但是,在EffectiveJava中,反对使用clone方法来进行克隆,详情关注 谨慎重写 clone 方法

关于作者

来自一线程序员Seven的探索与实践,持续学习迭代中~

本文已收录于我的个人博客:https://www.seven97.top

公众号:seven97,欢迎关注~

标签:category,讲讲,对象,clone,a1,person,Animal,拷贝
From: https://www.cnblogs.com/seven97-top/p/18377073

相关文章

  • Python之可变对象及其引用、深拷贝和浅拷贝
    可变对象及其引用深拷贝和浅拷贝可变对象及其引用Python中,变量名关联有值时才存在,如x=5变量名没有关联到特定的类型,类型有关联的对象觉得变量创建后即与特定的Python对象相关联Python维护命名空间,其中改变名与变量关联。这种联系,称为“引用”,也就是变量名引用对象......
  • 关于C++函数返回值的拷贝优化问题
    在传统C++程序中,如果函数的返回值是一个对象的话,可能需要对函数中的局部对象进行拷贝。如果该对象很大的话,则程序的效率会降低。在C++11以后,出现的移动语义(MoveSemantic)及拷贝优化(CopyElision)都是解决这个问题的方法。本文试图以一个最简单的例子来说明这个问题。案例下面来看......
  • 怎么禁止员工通过U盘将文件拷贝出公司?禁止u盘拷贝的三种方法【网友:一眼惊艳!】
    数据安全是每家公司不可忽视的重要议题。随着移动存储设备的普及,如U盘、移动硬盘等,它们虽然为数据交换带来了极大便利,但同时也成为了数据泄露的一大风险点。为了维护公司的信息安全,禁止员工通过U盘将文件随意拷贝出公司成为了一项必要措施。今天,我们就来揭秘三种一眼惊艳的......
  • js深拷贝
    功能如下:WeakMap解决循环引用问题支持拷贝的数据类型:基本数据类型:number、string、boolean、undefined、null、symbol引用数据类型:Date、RegExp、Function、Object、Map、Set代码实现:1functionclone(obj,map=newWeakMap){2if(map.has(obj))......
  • C/C++ 拷贝构造函数 | 赋值构造函数 | 移动构造函数 | 移动赋值构造函数
    文章目录前言1.拷贝构造函数(CopyConstructor)2.赋值构造函数(CopyAssignmentOperator)3.移动构造函数(MoveConstructor)4.移动赋值构造函数(MoveAssignmentOperator)总结前言C++中关于一个对象的构造,就有很多讲究。其中最常用的可能就是拷贝构造函数......
  • C:使用strncpy, memcpy 拷贝字符
    下面是一段C语言的代码片段。介绍如何使用strncpy,memcpy操作字符,字符串。 voidprintData(char*data,intlen,char*comment){if(comment){printf("==========printData:%s==========\n",comment);}for(inti=0;i<len;++i){if......
  • Python借助Selenium,保留原样式拷贝网站资源
    importurllib3fromseleniumimportwebdriverfrombs4importBeautifulSoupimportosimportrequestsfromurllib.parseimporturljoin,urlparseimportbase64importrefromrequests.adaptersimportHTTPAdapterfromrequests.packages.urllib3.util.retryim......
  • Scp 使用密码拷贝文件
    在Linux系统中,scp是一个非常好用的命令,可以用于在本地和远程计算机之间复制文件和目录。使用scp命令复制文件时,你可以使用两种方法来提供密码: 1.直接拷贝(手动输入密码)下面是使用scp命令复制文件并手动输入密码的方法:scpusername@remote_host:/path/to/remote/file/pa......
  • Unity 通过序列化和反序列化的方式创建深度拷贝Clone方法注意事项
    要将类标记为可序列化在类定义前添加 [Serializable] 属性。[Serializable]publicclassWorkorderAddData{publicintid;publicResponseresponse;[Serializable]publicclassResponse{publicintid;[Serializable]......
  • 数组拷贝System.arraycopy
    数组拷贝第一种方式:packagecom.coding.demo.concurrent;importjava.util.Arrays;/***使用Arrays.copyOf()*/publicclassTestArraysCopyOf{publicstaticvoidmain(String[]args){int[]src={1,2,3,4,5,6,7,8,9};int[]dest=Arrays......