精华内容
下载资源
问答
  • dict2 = dict1.copy() #浅拷贝 拷贝一级对象,但是如果字典里有个列表,改变dict1中这个列表的值的时候,dict2相应改变,可以自己递归实现深拷贝 import copy dict2 = copy.deepcopy(dict1)#深拷贝所有级别的对象都...

    python字典 直接赋值 copy deepcopy

    dict2 = dict1 #对象的引用
    dict2 = dict1.copy() #浅拷贝 拷贝一级对象,但是如果字典里有个列表,改变dict1中这个列表的值的时候,dict2相应改变,可以自己递归实现深拷贝
    import copy
    dict2 = copy.deepcopy(dict1)#深拷贝所有级别的对象都拷贝
    
    
    浅拷贝只拷贝父对象的意思
    a = {1: [1,2,3]}
    b = a.copy() #a, b分别为({1: [1, 2, 3]}, {1: [1, 2, 3]})
    a[1].append(4) #a, b分别为({1: [1, 2, 3, 4]}, {1: [1, 2, 3, 4]})

    OpenCV mat 直接赋值//Mat image1(image) ;//仅是创建了Mat的头部分,image1与image共享数据区

    cv::Mat image;
    cv::Mat image1(image) ;//仅是创建了Mat的头部分,image1与image共享数据区
    cv::Mat image1 = image ;//仅是创建了Mat的头部分,image1与image共享数据区  
    cv::Mat image1 = image.clone() ;//深拷贝,把image中的所有信息拷贝到image1中
    cv::Mat image1;
    image.copyTo(image1) ;//拷贝image的数据区到image1中,在拷贝数据前会有一步:image1.create(this->size , this->type)
    
    //注意 cv::Mat 作为函数参数,无论写不写& ,看似传值或者传引用这种方式,原图都会发生改变,不使用clone或者copyto或者手动赋值,都不是深拷贝,都共享数据区

    c++ 拷贝构造函数的浅拷贝和深拷贝

    c++中类对象的默认拷贝构造函数是浅拷贝,类a=类b,两个类的成员变量指向同一块内存,如果类b析构了之后,类a的成员变量还指向原来那块内存,就会有野指针的问题,会出错。

    想要深拷贝,应该自己自定义拷贝构造函数,为对应的成员变量开辟新空间。这才是好的编程习惯。

    //深拷贝的例子
    class Example{
    public:
        Example(int num, char* str) {
            num_ = num;
            str_ = new char[num];
            strcpy(str_, str);
        }
        Example(const Example& e) {
            num_ = e.num;
            str_ = new char(num_);
            if(str_ != 0)
                strcpy(str_, e.str);
        }
        ~Example(){
            delete str_;
        }
    
    private:
        int num_;
        char* str_;
    }
    展开全文
  • 文章目录1:深拷贝和浅拷贝的区别2:浅拷贝的使用 最近,学习和使用了react+ts的一些基本语法,使用过程中遇到了深拷贝和浅拷贝的问题:修改数据后,控制台打印数据已经改变,但是页面却不显示。多亏了同事的帮忙,...


    最近,学习和使用了react+ts的一些基本语法,使用过程中遇到了深拷贝和浅拷贝的问题:修改数据后,控制台打印数据已经改变,但是页面却不显示。多亏了同事的帮忙,一言难尽。下面把问题简单记录下。

    1:深拷贝和浅拷贝的区别

    浅拷贝:两个变量都是指向一个地址,改变了一个变量,另一个变量也随之改变。这也是浅拷贝带来的副作用,两个变量会相互影响到,因为它们指向同一个地址。

    深拷贝:两个变量互相独立,指向的是不同的地址。好处是其中一个变量改变了,另外一个变量不受影响。

    很显然,我们要想达到数据修改就能在页面显示,使用浅拷贝没错啦。

    2:浅拷贝的使用

    比如数据是dataSource,那么需要写成 {…dataSource}

    展开全文
  • 一篇文章彻底搞懂浅拷贝和深拷贝

    万次阅读 多人点赞 2018-07-16 16:36:32
    强烈推荐30个原生JavaScript的demo,包括canvas时钟特效、自定义视频播放器、搜索栏快速匹配、fetch访问资源、console调试...浅谈深拷贝和浅拷贝 深拷贝和浅拷贝的区别 为什么要使用深拷贝? 深拷贝的要求程度...

    强烈推荐30个原生JavaScript的demo,包括canvas时钟特效、自定义视频播放器、搜索栏快速匹配、fetch访问资源、console调试技巧等,先fork后学习,详见点击打开链接,欢迎点赞~~~谢谢,共同进步学习!

     

    由博主《前端初级工程师面试系列一JS基础》文章一JS变量类型引伸的考点,变量类型分为基本类型和引用类型,那么在变量拷贝赋值时,也是不一样的,分为浅拷贝和深拷贝,是面试中常考的知识点,也是实际开发中经常会用到的内容。

    目录

    正文

    前言: 最开始意识到深拷贝的重要性是在我使用redux的时候(react + redux), redux的机制要求在reducer中必须返回一个新的对象,而不能对原来的对象做改动,事实上,当时我当然不会主动犯这个错误,但很多时候,一不小心可能就会修改了原来的对象,例如:var newObj = obj; newObj.xxx = xxx  实际上,这个时候newObj和obj两个引用指向的是同一个对象,我修改了newObj,实际上也就等同于修改了obj,这,就是我和深浅拷贝的第一次相遇。

    深拷贝和浅拷贝的区别

    1.浅拷贝: 将原对象或原数组的引用直接赋给新对象,新数组,新对象/数组只是原对象的一个引用

    2.深拷贝: 创建一个新的对象和数组,将原对象的各项属性的“值”(数组的所有元素)拷贝过来,是“值”而不是“引用”

    为什么要使用深拷贝?

    我们希望在改变新的数组(对象)的时候,不改变原数组(对象)

    深拷贝的要求程度

    我们在使用深拷贝的时候,一定要弄清楚我们对深拷贝的要求程度:是仅“深”拷贝第一层级的对象属性或数组元素还是递归拷贝所有层级的对象属性和数组元素?

    怎么检验深拷贝成功

    改变任意一个新对象/数组中的属性/元素,     都不改变原对象/数组

    只对第一层级做拷贝

    深拷贝数组(只拷贝第一级数组元素) 

    1. 直接遍历
    var array = [1, 2, 3, 4];
    function copy (array) {
       let newArray = []
       for(let item of array) {
          newArray.push(item);
       }
       return  newArray;
    }
    var copyArray = copy(array);
    copyArray[0] = 100;
    console.log(array); // [1, 2, 3, 4]
    console.log(copyArray); // [100, 2, 3, 4]

    该方法不做解释(逃...)

    2. slice()

    var array = [1, 2, 3, 4];
    var copyArray = array.slice();
    copyArray[0] = 100;
    console.log(array); // [1, 2, 3, 4]
    console.log(copyArray); // [100, 2, 3, 4]

    slice() 方法返回一个从已有的数组中截取一部分元素片段组成的新数组(不改变原来的数组!)

    用法:array.slice(start,end) start表示是起始元素的下标, end表示的是终止元素的下标

    当slice()不带任何参数的时候,默认返回一个长度和原数组相同的新数组

    3. concat()

    var array = [1, 2, 3, 4];
    var copyArray = array.concat();
    copyArray[0] = 100;
    console.log(array); // [1, 2, 3, 4]
    console.log(copyArray); // [100, 2, 3, 4]

    concat() 方法用于连接两个或多个数组。( 该方法不会改变现有的数组,而仅仅会返回被连接数组的一个副本。)

    用法:array.concat(array1,array2,......,arrayN)

    因为我们上面调用concat的时候没有带上参数,所以var copyArray = array.concat();实际上相当于var copyArray = array.concat([]);也即把返回数组和一个空数组合并后返回

     

    但是,事情当然不会这么简单,我上面的标题是 “深拷贝数组(只拷贝第一级数组元素)”,这里说的意思是对于一级数组元素是基本类型变量(如number,String,boolean)的简单数组, 上面这三种拷贝方式都能成功,但对第一级数组元素是对象或者数组等引用类型变量的数组,上面的三种方式都将失效,例如:

    
    var array = [
      { number: 1 },
      { number: 2 },
      { number: 3 }
    ];
    var copyArray = array.slice();
    copyArray[0].number = 100;
    console.log(array); //  [{number: 100}, { number: 2 }, { number: 3 }]
    console.log(copyArray); // [{number: 100}, { number: 2 }, { number: 3 }]

    深拷贝对象

    1.直接遍历

    var obj = {
      name: '彭湖湾',
      job: '学生'
    }
    
    function copy (obj) {
       let newObj = {};
         for (let item in obj ){
           newObj[item] = obj
         }
         return newObj;
    }
     var copyObj = copy(obj);
    copyObj.name = '我才不是彭湖湾呢! 哼 (。・`ω´・)';
    console.log(obj); // {name: "彭湖湾", job: "学生"}
    console.log(copyObj); // {name: "我才不是彭湖湾呢! 哼 (。・`ω´・)", job: Object}

    该方法不做解释(逃...)

    2.ES6的Object.assign

    var obj = {
      name: '彭湖湾',
      job: '学生'
    }
    var copyObj = Object.assign({}, obj);
    copyObj.name = '我才不叫彭湖湾呢! 哼  (。・`ω´・)';
    console.log(obj);   // {name: "彭湖湾", job: "学生"}
    console.log(copyObj);  // {name: "我才不叫彭湖湾呢! 哼  (。・`ω´・)", job: "学生"}

    Object.assign:用于对象的合并,将源对象(source)的所有可枚举属性,复制到目标对象(target),并返回合并后的target

    用法: Object.assign(target, source1, source2);  所以 copyObj = Object.assign({}, obj);  这段代码将会把obj中的一级属性都拷贝到 {}中,然后将其返回赋给copyObj

    3.ES6扩展运算符:

    var obj = {
        name: '彭湖湾',
        job: '学生'
    }
    var copyObj = { ...obj }
    copyObj.name = '我才不叫彭湖湾呢! 哼  (。・`ω´・)'
    console.log(obj.name) //   彭湖湾
    console.log(copyObj.name)  // 我才不叫彭湖湾呢! 哼  (。・`ω´・)

    扩展运算符(...)用于取出参数对象的所有可遍历属性,拷贝到当前对象之中

    ⚠️注意:实际上,无论是使用扩展运算符(...)还是解构赋值,对于引用类型都是浅拷贝。所以在使用splice()、concat()、...对数组拷贝时,只有当数组内部属性值不是引用类型是,才能实现深拷贝。对多层嵌套对象,也即是存在,很遗憾,上面三种方法,都会失败:

    var obj = {
       name: {
          firstName: '彭',
          lastName: '湖湾'
       },
       job: '学生'
    }
    var copyObj = Object.assign({}, obj)
    copyObj.name.lastName = '湖水的小浅湾';
    console.log(obj.name.lastName); // 湖水的小浅湾
    console.log(copyObj.name.lastName); // 湖水的小浅湾

    拷贝所有层级

    有没有更强大一些的解决方案呢?使得我们能够

    1.不仅拷贝第一层级,还能够拷贝数组或对象所有层级的各项值

    2. 不是单独针对数组或对象,而是能够通用于数组,对象和其他复杂的JSON形式的对象

    请看下面:

    下面这一招可谓是“一招鲜,吃遍天”

    1.JSON.parse(JSON.stringify(XXXX))

    var array = [
        { number: 1 },
        { number: 2 },
        { number: 3 }
    ];
    var copyArray = JSON.parse(JSON.stringify(array))
    copyArray[0].number = 100;
    console.log(array); //  [{number: 1}, { number: 2 }, { number: 3 }]
    console.log(copyArray); // [{number: 100}, { number: 2 }, { number: 3 }]

    JSON.parse() 方法用于将一个 JSON 字符串转换为对象--(反序列化)

    JSON.stringify() 方法是将一个JavaScript值(对象或者数组)转换为一个 JSON字符串--(序列化)

    序列化的缺点:

    1. 不支持基本数据类型的undefined,序列化后将其省略
    2. 不支持函数
    3. Nan,Infinity序列化的结果是null

    能用大招杀的就不要用q杀嘛!!

    2.手动写递归

    你说啥? 你说上面的那种方法太无脑,  一定要自己写一段递归才有做技术的感觉? OK成全你!

    let array = [
       { number: 1 },
       { number: 2 },
       { number: 3 }
    ];
    function copy (obj) {
            //首先判断需要拷贝的“东西”是什么类型
            if(typeof obj !== 'object' || obj == null){
                return;
            }
            let newobj = obj.constructor === Array ? [] : {};
            //obj是数组类型,下面的i就是index;obj是对象,i就是key
            for(let i in obj){
               newobj[i] = typeof obj[i] === 'object' ? copy(obj[i]) : obj[i];
            }
            return newobj
    }
    
    let copyArray = copy(array)
    copyArray[0].number = 100;
    console.log(array); //  [{number: 1}, { number: 2 }, { number: 3 }]
    console.log(copyArray); // [{number: 100}, { number: 2 }, { number: 3 }]

    【注意】上文的所有的示例都忽略了一些特殊的情况: 对对象/数组中的Function,正则表达式等特殊类型的拷贝

    上述代码中还隐藏这两个知识点:

    (1)obj == null 为何不是 obj === null?

    obj==null =>obj为null 或者 obj 为undefined,因为null == undefined,这样写能默认处理两种情况,obj===null ,成立前提只有obj是null

    (2)怎样判断一个对象是不是数组?

    先上方法(确定的方法有两种)

    1. 根据对象的class属性来判断,跨原型链调用toString()方法。  Object. prototype.toString.call(obj)===[ object Array]
    2. Array.isArray直接判断   Array. isArray(obj)。

    补充:

    • 推荐文章:为什么用Object.prototype.toString.call(obj)检测对象类型

      toString为Object的原型方法,返回一个用来描述该对象的字符串,所以可以调用对象原型方法toString()来探明对象的信息。
      那么原型方法怎么调用呢,利用call,将其this指向需要判断的对象, 就可以用toString()方法了。
      let arr=[1,2,3];
      console.log(Array.prototype.hasOwnProperty("toString"));//true
      console.log(arr.toString());//1,2,3 arr作为对象Object的实例,重写了toString()方法。
      delete Array.prototype.toString;//delete操作符可以删除实例属性
      console.log(Array.prototype.hasOwnProperty("toString"));//false
      console.log(arr.toString());//"[object Array]" 
      // 由于删除了实例对象中的toString()方法,找不到,顺着原型链往上走,就调用了对象Object的方法,返回的结果就和Array.prototype.toString(arr)一样的。
    • 根据构造函数来判断 instanceof 操作符可以来表示实例是否属于某个构造函数创建的。

    这种方法有一个问题,就是验证不够严格。 即使对象创建时不是使用数组创建的,但是只要原型链上有数组类型,也认为是数组,亦或者,即便创建时是数组创建,但其原型上有对象类型,便不再被认为是数组。

    推荐文章

    (3)for...in 和for...of,forEach的区别,for...in用于对象复制时需要注意什么?

    for... in特点

    • 遍历对象返回的对象的key值,遍历数组返回的数组的下标(key)。

    • for ... in 会遍历原型上的属性值

    • 遍历返回数据是乱序

    总结一句: for in 循环特别适合遍历对象。

    for... of特点

    • for of遍历的只是数组内的元素,而不包括数组的原型属性method和索引name

    • for ... in 会遍历原型上的属性值

    • 遍历返回数据是乱序

    • for of 不同与 forEach, 它可以与 break、continue和return 配合使用,也就是说 for of 循环可以随时退出循环。

    总结一句: for of 比较适合遍历数组,及其他具有遍历器的集合

    forEach特点

    • 使用foreach遍历数组的话,使用break不能中断循环,使用return也不能返回到外层函数。forEach与break和return 不搭

    • forEach()无法在所有元素都传递给调用的函数之前终止遍历

    for…in循环可应用于对象的复制,不过其有一个缺点,就是会从原型属性里继承prototype()属性。 

    例如: 

    let array = [1,2,3,4,5] 
    Array.prototype.age = 13;
    var result = []; 
    for(let i in array){ 
        result.push(array[i]); 
    } 
    alert(result.join(“,”)); 
    result返回结果【1,2,3,4,5,13】 

    如何避免从原型属性里继承prototype()属性,这里使用hasOwnProperty(name),该函数指示一个对象自身(不包括原型链)是否具有指定名称的属性。如果有返回true,如果没有返回false。 

    let array = [1,2,3,4,5] 
    Array.prototype.age = 13; 
    var result = []; 
    for(let i in array){ 
    if(array.hasOwnProperty(i)){ 
        result.push(array[i]); 
    } 
    alert(result.join(“,”)); 
    } 
    result返回结果【1,2,3,4,5】

    所以上面的深拷贝代码应优化为如下: 

    let array = [
       { number: 1 },
       { number: 2 },
       { number: 3 }
    ];
    function copy (obj) {
            //首先判断需要拷贝的“东西”是什么类型
            if(typeof obj !== 'object' || obj == null){
                return;
            }
            let newobj = Array.isArray(obj) ? [] : {};
            //obj是数组类型,下面的i就是index;obj是对象,i就是key
            for(let i in obj){
               if(obj.hasOwnProperty(i)){
                   newobj[i] = typeof obj[i] === 'object' ? copy(obj[i]) : obj[i];
                }
            }
            return newobj
    }
    
    let copyArray = copy(array)
    copyArray[0].number = 100;
    console.log(array); //  [{number: 1}, { number: 2 }, { number: 3 }]
    console.log(copyArray); // [{number: 100}, { number: 2 }, { number: 3 }]

    存在大量深拷贝需求的代码——immutable提供的解决方案

    实际上,即使我们知道了如何在各种情况下进行深拷贝,我们也仍然面临一些问题: 深拷贝实际上是很消耗性能的。(我们可能只是希望改变新数组里的其中一个元素的时候不影响原数组,但却被迫要把整个原数组都拷贝一遍,这不是一种浪费吗?)所以,当你的项目里有大量深拷贝需求的时候,性能就可能形成了一个制约的瓶颈了。

    immutable的作用

    通过immutable引入的一套API,实现:

    1.在改变新的数组(对象)的时候,不改变原数组(对象)

    2.在大量深拷贝操作中显著地减少性能消耗

    先睹为快:

    const { Map } = require('immutable')
    const map1 = Map({ a: 1, b: 2, c: 3 })
    const map2 = map1.set('b', 50)
    map1.get('b') // 2
    map2.get('b') // 50
    展开全文
  • Java 深拷贝 和 浅拷贝 clone

    万次阅读 2019-11-27 16:15:22
    关于Java的深拷贝和浅拷贝,简单来说就是创建一个和已知对象一模一样的对象。可能日常编码过程中用的不多,但是这是一个面试经常会问的问题,而且了解深拷贝和浅拷贝的原理,对于Java中的所谓值传递或者引用传递将会...

    关于Java的深拷贝和浅拷贝,简单来说就是创建一个和已知对象一模一样的对象。可能日常编码过程中用的不多,但是这是一个面试经常会问的问题,而且了解深拷贝和浅拷贝的原理,对于Java中的所谓值传递或者引用传递将会有更深的理解。

    1、创建对象的5种方式

    1. 通过 new 关键字

      这是最常用的一种方式,通过 new 关键字调用类的有参或无参构造方法来创建对象。比如 Object obj = new Object();

    2. 通过 Class 类的 newInstance() 方法

      这种默认是调用类的无参构造方法创建对象。比如 Person p2 = (Person) Class. forName(“com.ys.test. Person”). newInstance();

    3. 通过 Constructor 类的 newInstance 方法

      这和第二种方法类时,都是通过反射来实现。通过 java.lang.relect.Constructor 类的 newInstance() 方法指定某个构造器来创建对象。
        Person p3 = (Person) Person.class.getConstructors()[0].newInstance();
        实际上第二种方法利用 Class 的 newInstance() 方法创建对象,其内部调用还是 Constructor 的 newInstance() 方法。

    4. 利用 Clone 方法

      Clone 是 Object 类中的一个方法,通过 对象A.clone() 方法会创建一个内容和对象 A 一模一样的对象 B,clone 克隆,顾名思义就是创建一个一模一样的对象出来。
        Person p4 = (Person) p3.clone();

    5. 序列化

      序列化是把堆内存中的 Java 对象数据,通过某种方式把对象存储到磁盘文件中或者传递给其他网络节点(在网络上传输)。而反序列化则是把磁盘文件中的对象数据或者把网络节点上的对象数据,恢复成Java对象模型的过程。序列化

    Java 基本复制方法

    java赋值是复制对象引用,如果我们想要得到一个对象的副本,使用赋值操作是无法达到目的的:
    修改新对象的值会同时修改旧对象的值。

    public class Client
    {
    	public static void main(String[] args) throws CloneNotSupportedException
    	{
    		Person person = new Person(15, "sowhat", new Address("河北", "建华南大街"));
    		Person p1 = person;
    		p1.setAge(45);
    		System.out.println(p1.hashCode());
    		System.out.println(person.hashCode());
    		System.out.println("================");
    		System.out.println(p1.display());
    		System.out.println(person.display());
    	}
    }
    

    Clone 方法

    如果创建一个对象的新的副本,也就是说他们的初始状态完全一样,但以后可以改变各自的状态,而互不影响,就需要用到java中对象的复制,如原生的clone()方法。
    本次讲解的是 Java 的深拷贝和浅拷贝,其实现方式正是通过调用 Object 类的 clone() 方法来完成。在 Object.class 类中,源码为:

    /**
         * ...
         * performs a "shallow copy" of this object, not a "deep copy" operation.
         * 上面这里已经说明了,clone()方法是浅拷贝,而不是深拷贝
         * @see java.lang.Cloneable
         */
        protected native Object clone() throws CloneNotSupportedException;
    

    这是一个用 native 关键字修饰的方法,关于native关键字有一篇博客专门有介绍,不理解也没关系,只需要知道用 native 修饰的方法就是告诉操作系统,这个方法我不实现了,让操作系统去实现(参考JNI)。具体怎么实现我们不需要了解,只需要知道 clone方法的作用就是复制对象,产生一个新的对象。那么这个新的对象和原对象是什么关系呢

    基本类型和引用类型

    这里再给大家普及一个概念,在 Java 中基本类型和引用类型的区别。在 Java 中数据类型可以分为两大类:基本类型和引用类型。
    基本类型也称为值类型,分别是字符类型 char,布尔类型 boolean以及数值类型 byte、short、int、long、float、double。
    引用类型则包括类、接口、数组、枚举等。
      Java 将内存空间分为堆和栈。基本类型直接在栈 stack中存储数值,而引用类型是将引用放在栈中,实际存储的值是放在堆 heap中,通过栈中的引用指向堆中存放的数据。
      在这里插入图片描述

    上图定义的 a 和 b 都是基本类型,其值是直接存放在栈中的;而 c 和 d 是 String 声明的,这是一个引用类型,引用地址是存放在栈中,然后指向堆的内存空间
      下面 d = c;这条语句表示将 c 的引用赋值给 d,那么 c 和 d 将指向同一块堆内存空间。

    浅拷贝

    接下来用代码看看浅拷贝的效果。

    package mytest;
    @Data//lombok注解
    class Person implements Cloneable
    {
    	private int age;
    	private String name;
    	private Address address;
    
    	public Person(int age, String name, Address address)
    	{
    		this.age = age;
    		this.name = name;
    		this.address = address;
    	}
    
    	@Override
    	protected Object clone() throws CloneNotSupportedException
    	{
    		return super.clone();
    	}
    
    	public String display()
    	{
    		return "Person [age=" + age + ", name=" + name + ", address=" + address + "]";
    	}
    }
    
    @Data//lombok注解
    class Address
    {
    	private String province;
    	private String street;
    
    	public Address(String province, String street)
    	{
    		this.province = province;
    		this.street = street;
    	}
    	@Override
    	public String toString()
    	{
    		return "Address [province=" + province + ", street=" + street + "]";
    	}
    }
    
    public class Client
    {
    	public static void main(String[] args) throws CloneNotSupportedException
    	{
    		Person person = new Person(15, "sowhat", new Address("河北", "建华南大街"));
    		Person clonePerson = (Person) person.clone();
    		System.out.println(person);
    		System.out.println(clonePerson);
    
    		// 信息完全一样
    		System.out.println(person.display());
    		System.out.println(clonePerson.display());
    		System.out.println("信息完全一致");
    
    		System.out.println("原始年龄:" + person.getAge());
    		System.out.println("克隆后原始年龄:" + clonePerson.getAge());
    		System.out.println("年龄完全一样");
    
    		System.out.println("原始名字哈希值:" + person.getName().hashCode());
    		System.out.println("克隆后名字哈希值:" + clonePerson.getName().hashCode());
    		System.out.println("字符串哈希值完全一样");
    
    		clonePerson.setName("xiaomai");
    		clonePerson.setAge(20);
    		clonePerson.getAddress().setStreet("天府四街");
    		System.out.println(clonePerson.display());
    		System.out.println(person.display());
    		System.out.println("年龄跟姓名 是完全的深拷贝 副本跟原值无关的!");
    		System.out.println("地址信息的修改是浅拷贝  ");
    
    	}
    }
    

    结果如下:

    mytest.Person@15f550a
    mytest.Person@6b2d4a
    Person [age=15, name=sowhat, address=Address [province=河北, street=建华南大街]]
    Person [age=15, name=sowhat, address=Address [province=河北, street=建华南大街]]
    信息完全一致
    原始年龄:15
    克隆后原始年龄:15
    年龄完全一样
    原始名字哈希值:-1432601412
    克隆后名字哈希值:-1432601412
    字符串哈希值完全一样
    Person [age=20, name=xiaomai, address=Address [province=河北, street=中山路]]
    Person [age=15, name=sowhat, address=Address [province=河北, street=中山路]]

    结论:

    1. 原对象与新对象是两个不同的对象。
    2. 拷贝出来的新对象与原对象内容一致
    3. 接着将新对象里面的信息进行了修改,然后输出发现原对象里面的部分信息也跟着变了。其中 基本类型跟 String类型的改变不会影响到 原始对象的改变。而其他的Ojbect 类型改变的时候会影响到原始数据。
      上面的结论称为浅拷贝
      浅拷贝:创建一个新对象,然后将当前对象的非静态字段复制到该对象,如果字段类型是值类型(基本类型跟String)的,那么对该字段进行复制;如果字段是引用类型的,则只复制该字段的引用而不复制引用指向的对象(也就是只复制对象的地址)。此时新对象里面的引用类型字段相当于是原始对象里面引用类型字段的一个副本,原始对象与新对象里面的引用字段指向的是同一个对象
      因此,修改clonePerson里面的address内容时,原person里面的address内容会跟着改变。

    深拷贝

    了解了浅拷贝,那么深拷贝是什么也就很清楚了。那么该如何实现深拷贝呢? Object 类提供的 clone 是只能实现 浅拷贝的。,即将引用类型的属性内容也拷贝一份新的。那么,实现深拷贝我这里收集到两种方式:
    第一种是给需要拷贝的引用类型也实现Cloneable接口并覆写clone方法;
    第二种则是利用序列化。接下来分别对两种方式进行演示。

    深拷贝-clone方式

    对于以上演示代码,利用clone方式进行深拷贝无非就是将Address类也实现Cloneable,然后对Person的clone方法进行调整。让每个引用类型属性内部都重写clone() 方法,既然引用类型不能实现深拷贝,那么我们将每个引用类型都拆分为基本类型,分别进行浅拷贝。比如上面的例子,Person 类有一个引用类型 Address(其实String 也是引用类型,但是String类型有点特殊,后面会详细讲解),我们在 Address 类内部也重写 clone 方法。如下:

    package mytest;
    @Data//lombok注解
    class Person implements Cloneable
    {
    	private int age;
    	private String name;
    	private Address address;
    	protected int abc = 12;
    
    	public Person(int age, String name, Address address)
    	{
    		this.age = age;
    		this.name = name;
    		this.address = address;
    	}
    	
    	@Override  // clone 重载
    	protected Object clone() throws CloneNotSupportedException
    	{
    		Person person = (Person) super.clone();
    		//手动对address属性进行clone,并赋值给新的person对象
    		person.address = (Address) address.clone();
    		return person;
    	}
    
    	public String display()
    	{
    		return "Person [age=" + age + ", name=" + name + ", address=" + address + "]";
    	}
    }
    
    @Data//lombok注解
    class Address implements  Cloneable
    {
    	private String province;
    	private String street;
    
    	public Address(String province, String street)
    	{
    		this.province = province;
    		this.street = street;
    	}
    	// 深拷贝时添加
    	@Override
    	protected Object clone() throws CloneNotSupportedException
    	{
    		return super.clone();
    	}
    	
    	@Override
    	public String toString()
    	{
    		return "Address [province=" + province + ", street=" + street + "]";
    	}
    }
    
    public class Client
    {
    	public static void main(String[] args) throws CloneNotSupportedException
    	{
    		Person person = new Person(15, "sowhat", new Address("河北", "建华南大街"));
    		Person p1 = person;
    		p1.setAge(45);
    		System.out.println(p1.hashCode());
    		System.out.println(person.hashCode());
    
    		System.out.println(p1.display());
    		System.out.println(person.display());
    		System.out.println("-----------");
    
    		Person clonePerson = (Person) person.clone();
    		System.out.println(person);
    		System.out.println(clonePerson);
    
    		// 信息完全一样
    		System.out.println(person.display());
    		System.out.println(clonePerson.display());
    		System.out.println("信息完全一致");
    
    		System.out.println("原始年龄:" + person.getAge());
    		System.out.println("克隆后原始年龄:" + clonePerson.getAge());
    		System.out.println("年龄完全一样");
    
    		System.out.println("原始名字哈希值:" + person.getName().hashCode());
    		System.out.println("克隆后名字哈希值:" + clonePerson.getName().hashCode());
    		System.out.println("字符串哈希值完全一样");
    
    		clonePerson.setName("xiaomai");
    		clonePerson.setAge(20);
    		clonePerson.getAddress().setStreet("中山路");
    		System.out.println(clonePerson.display());
    		System.out.println(person.display());
    		System.out.println("年龄跟姓名 是完全的深拷贝 副本跟原值无关的!");
    		System.out.println("地址信息的修改是浅拷贝  ");
    
    	}
    }
    

    但是这种做法有个弊端,这里我们Person 类只有一个 Address 引用类型,而 Address 类没有,所以我们只用重写 Address 类的clone 方法,但是如果 Address 类也存在一个引用类型,那么我们也要重写其clone 方法,这样下去,有多少个引用类型,我们就要重写多少次,如果存在很多引用类型,那么代码量显然会很大,所以这种方法不太合适。

    利用序列化

    序列化是将对象写到流中便于传输,而反序列化则是把对象从流中读取出来。这里写到流中的对象则是原始对象的一个拷贝,因为原始对象还存在 JVM 中,所以我们可以利用对象的序列化产生克隆对象,然后通过反序列化获取这个对象。
    注意每个需要序列化的类都要实现 Serializable 接口,如果有某个属性不需要序列化,可以将其声明为 transient,即将其排除在克隆属性之外。

    package mytest;
    
    import java.io.ByteArrayInputStream;
    import java.io.ByteArrayOutputStream;
    import java.io.ObjectInputStream;
    import java.io.ObjectOutputStream;
    import java.io.Serializable;
    
    /**
     * 利用序列化和反序列化进行对象的深拷贝
     * @author  ljj
     */
    class DeepClone implements Serializable
    {
    	private static final long serialVersionUID = 1412L;
    	
    	public Object deepClone() throws Exception
    	{
    		//序列化
    		ByteArrayOutputStream bos = new ByteArrayOutputStream();
    		ObjectOutputStream oos = new ObjectOutputStream(bos);
    		oos.writeObject(this);
    		//反序列化
    		ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray());
    		ObjectInputStream ois = new ObjectInputStream(bis);
    		return ois.readObject();
    	}
    }
    @Data
    class Person extends DeepClone
    {
    	private static final long serialVersionUID = 1L;
    	private int age;
    	private String name;
    	private Address address;
    
    	public Person(int age, String name, Address address)
    	{
    		this.age = age;
    		this.name = name;
    		this.address = address;
    	}
    
    	public String display()
    	{
    		return "Person [age=" + age + ", name=" + name + ", address=" + address + "]";
    	}
    }
    
    @Data
    class Address extends DeepClone
    {
    	private static final long serialVersionUID = 1412L;
    	private String province;
    	private String street;
    
    	public Address(String province, String street)
    	{
    		this.province = province;
    		this.street = street;
    	}
    
    	@Override
    	public String toString()
    	{
    		return "Address [province=" + province + ", street=" + street + "]";
    	}
    
    	public void setStreet(String street)
    	{
    		this.street = street;
    	}
    }
    
    public class Client
    {
    	public static void main(String[] args) throws Exception
    	{
    		Person person = new Person(15, "sowhat", new Address("河北", "建华南大街"));
    
    		Person clonePerson = (Person) person.deepClone();
    
    		System.out.println(person);
    		System.out.println(clonePerson);
    
    		System.out.println(person.display());
    		System.out.println(clonePerson.display());
    
    		clonePerson.setName("xiaomai");
    		clonePerson.setAge(20);
    		Address address = clonePerson.getAddress();
    		address.setStreet("中山路");
    		System.out.println(clonePerson.display());
    		System.out.println(person.display());
    
    	}
    }
    

    展开全文
  • 深拷贝和浅拷贝

    万次阅读 多人点赞 2019-05-12 17:28:52
    深拷贝和浅拷贝的知识涉及到堆栈的概念。 堆栈的概念: 基本类型: 名值存储在栈内存中,例如: let a = 1; 引用类型: 名存在栈内存中,值存在于堆内存中,但是栈内存会提供一个引用的地址指向堆内存中的值; ...
  • BeanUtils.copyProperties的使用(深拷贝,浅拷贝)

    万次阅读 多人点赞 2020-03-18 23:30:05
    这里说的是spring的BeanUtils.copyProperties。 场景 开发中经常遇到,把父类的属性拷贝到子类中。通常有2种方法: 1、一个一个set 2、用BeanUtils.copyProperties ...浅拷贝: 只是调用子对象的set...
  • Js复制对象/克隆对象 Js浅拷贝与深拷贝 浅拷贝和深拷贝的实现方法 前言 学习Js克隆一个对象,作为准备工作,需要理解Js中的数据类型和按值传递:Js中的数据类型和按值传递 浅拷贝最后两种方法不理解的话,可以读es5...
  • 终于弄清楚JS的深拷贝和浅拷贝了-读这一篇就够了

    万次阅读 多人点赞 2018-07-27 17:22:14
    今天,CVTE面试官问了深拷贝和浅拷贝的问题 我的回答是:浅拷贝是拷贝了对象的引用,当原对象发生变化的时候,拷贝对象也跟着变化;深拷贝是另外申请了一块内存,内容和原对象一样,更改原对象,拷贝对象不会发生...
  • 浅拷贝与深拷贝

    千次阅读 2021-02-11 11:54:43
    浅拷贝 对象本身属性使用对象拷贝,对象引用的属性使用引用拷贝 注意:本实验存在两个对象EmailEntity 和 JSPClassEntity 对象 其中EmailEntity 引用了 JSPClassEntity 对象。 实现方式:实体对象实
  • 深拷贝 vs 浅拷贝 浅拷贝 概念 复制基本类型的属性;引用类型的属性复制,复制栈中的变量 和 变量指向堆内存中的对象的指针,不复制堆内存中的对象。 如图: ​ 特点 ​ 1.对于基本数据类型的成员对象,因为基础...
  • 深拷贝浅拷贝

    2019-10-09 17:54:26
    深拷贝与浅拷贝通俗易懂的来讲 深拷贝和浅拷贝:  浅拷贝就比如像引用类型,而深拷贝就比如值类型。  浅拷贝是指源对象与拷贝对象共用一份实体,仅仅是引用的变量不同(名称不同)。对其中任何一个对象的改动都会...
  • Golang深拷贝浅拷贝

    万次阅读 2019-05-30 23:19:37
    Golang深拷贝浅拷贝 在了解原型设计模式之前我们需要新知道Golang的深拷贝与浅拷贝之间的区别。 推荐大家新看看Slice 和 Map那么常见的坑:https://blog.csdn.net/weixin_40165163/article/details/90707593 ...
  • lodash深拷贝浅拷贝

    2020-11-30 01:27:24
    例子,v1浅拷贝出v11,修改v11不会修改到v1 对象转json,json转对象,深拷贝 stringify()将对象转json字符串,parse()将json转js对象 npm i lodash 安装依赖 import _ from 'lodash' 导入依赖 const v11= _....
  • 文章目录深拷贝和浅拷贝浅拷贝深拷贝小结切片和字典 深拷贝和浅拷贝 浅拷贝 什么是浅拷贝 python中的赋值语句一般都是变量指向数据(是一个索引) 浅拷贝copy.copy() 注意:python中的浅拷贝和赋值语句会...
  • 浅谈深拷贝和浅拷贝(js)

    万次阅读 多人点赞 2018-10-05 15:11:06
    如何区分深拷贝与浅拷贝?简单点来说,就是假设B复制了A,当修改A时,看B是否会发生变化,如果B也跟着变了,说明是浅拷贝,如果B没变,那就是深拷贝。深入点来说,就是B复制了A,如果B复制的是A的引用,那就是浅拷贝...
  • python浅拷贝和深度拷贝

    万次阅读 2019-07-05 09:36:21
    通过代码和结果来理解浅拷贝和深度拷贝,先来看看python中有那些常见的浅拷贝方法: 1.使用数据类型本身的构造器: l1 = [1,2,3] l2 = list(l1) l1 == l2 #True l1 is l2 #False #l2就是l1的浅拷贝,set、dict与...
  • 浅拷贝和深拷贝

    2020-08-10 13:54:10
    浅拷贝和深拷贝 浅拷贝的概念 拷贝就是拷贝指向对象的指针,意思就是说:拷贝出来的目标对象的指针和源对象的指针指向的内存空间是同一块空间,浅拷贝只是一种简单的拷贝,让几个对象公用一个内存,然而当内存销毁...
  • 深拷贝与浅拷贝

    千次阅读 2020-06-28 17:04:11
    无论是深拷贝还是浅拷贝,都需要实现 Cloneable 接口,并覆写 clone() 方法。 浅拷贝 @Override public Object clone() { //浅拷贝 try { // 直接调用父类的clone()方法 return super.clone(); } catch ...
  • ECMAScript中的浅拷贝和深拷贝

    万次阅读 2020-02-07 19:09:27
    浅拷贝:浅拷贝是将原始对象中的数据型字段拷贝到新对象中去,将引用型字段的“引用”复制到新对象中去,不把“引用的对象”复制进去,所以原始对象和新对象引用同一对象,新对象中的引用型字段发生变化会导致原始...
  • 浅谈浅拷贝和深拷贝

    2018-04-16 22:45:27
    浅拷贝是对一个对象的顶层拷贝,通俗的理解是:拷贝了引用,并没有拷贝内容,复制的是第一层。深拷贝是对一个对象所有层次的拷贝,递归所有的数据,全部复制一份,。对不可变类型来说,深拷贝和浅拷贝的引用地址都是同...
  • JS深拷贝和浅拷贝

    2020-11-18 20:18:39
    文章目录定义浅拷贝对象的浅拷贝数组的浅拷贝深拷贝json序列化jQuery深拷贝lodash函数库实现深拷贝手动写递归方式 定义 什么是浅拷贝:两者指向同一个对象。 浅拷贝 对象的浅拷贝 1、对象的直接遍历赋值。 2、ES6...
  • 深拷贝和浅拷贝区别是什么?

    万次阅读 多人点赞 2019-06-18 15:44:47
    深拷贝和浅拷贝区别是什么? 复制一个 Java 对象 浅拷贝:复制基本类型的属性;引用类型的属性复制,复制栈中的变量 和 变量指向堆内存中的对象的指针,不复制堆内存中的对象。 深拷贝:复制基本类型的属性;...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 27,260
精华内容 10,904
关键字:

浅拷贝