Spring学习笔记(一)【BeanUtils.copyProperties方法】

2023-11-06

Spring下的BeanUtils.copyProperties方法是深拷贝还是浅拷贝?

一、浅拷贝深拷贝的理解


简单地说,拷贝就是将一个类中的属性拷贝到另一个中,对于BeanUtils.copyProperties来说,必须保证属性名和类型是相同的,因为它是根据get和set方法来赋值的

1.1、浅拷贝

浅拷贝对于基本数据类型就是直接进行值传递,在内存的另一个空间内存放,修改这个值不会影响到拷贝源的值
浅拷贝对于引用数据类型就是进行的是地址传递,并没有对该对象重新开辟一个内存空间进行存放,所以对于引用数据类型的浅拷贝就相当于两个引用指向了同一个内存地址

浅拷贝可以理解为如果是引用类型,那么目标对象拷贝的只是源对象的地址,无论目标对象还是源对象改变,他们都会一起改变

1.2、深拷贝

深拷贝就是将目标对象的属性全部复制一份给源对象,复制完之后他们就是隔开的,没有任何关系,无论操作源对象还是目标对象都对另一个没有影响

无论是浅拷贝还是深拷贝,对于基本类型和String来说都是没有影响的,有影响的只有引用类型数据

在这里插入图片描述

二、测试浅拷贝与深拷贝


  • 浅拷贝
package com.vinjcent;

public class Main {

    public static void main(String[] args) {
        // 主人(正)
        Master main = new Master("男");
        // 宠物
        Pet p = new Pet("小狗", 5);

        main.setPet(p);

        // 浅拷贝一个"主"对象(副)
        Master vice = main.clone();

        System.out.println("主对象: " + main);
        System.out.println("副对象: " + vice);
        System.out.println("拷贝后对象的指针是否都指向同一个堆内存: " + (main == vice));
        System.out.println();

        // 修改副对象中的性别属性
        vice.setGender("女");
        System.out.println("主对象: " + main);
        System.out.println("副对象: " + vice);
        System.out.println("修改拷贝对象中的String类型的属性gender后是否相同: " + main.getGender().equals(vice.getGender()));
        System.out.println();


        // 修改副对象中引用对象中的年龄属性
        vice.getPet().setAge(10);
        System.out.println("主对象: " + main);
        System.out.println("副对象: " + vice);
        System.out.println("修改拷贝对象中的引用类型Pet后,是否在同一个堆内存当中: " + (main.getPet() == vice.getPet()));
        System.out.println();
        System.out.println("可以发现,它是对'主'对象的拷贝,它不会拷贝'主'对象深层次的可变对象,只做第一层的拷贝");
    }
}

// 宠物对象
class Pet {
    private String name;
    private int age;

    public Pet(String name, int age) {
        this.name = name;
        this.age = age;
    }

	// ...省略set、get方法

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

// 主人对象
class Master implements Cloneable {
    private String gender;

    private Pet pet;

    public Master(String gender) {
        this.gender = gender;
    }

	// ...省略set、get方法

    // 浅拷贝
    @Override
    public Master clone() {
        try {
            // TODO: copy mutable state here, so the clone can't change the internals of the original
            return (Master) super.clone();
        } catch (CloneNotSupportedException e) {
            throw new AssertionError();
        }
    }

    @Override
    public String toString() {
        return "Master{" +
                "gender='" + gender + '\'' +
                ", pet=" + pet +
                '}';
    }
}
  • 输出结果

在这里插入图片描述

在这里插入图片描述

  • 深拷贝
package com.vinjcent;

public class Main {

    public static void main(String[] args) {
        // 主人(正)
        Master main = new Master("男");
        // 宠物
        Pet p = new Pet("小狗", 5);

        main.setPet(p);

        // 深拷贝一个"主"对象(副)
        Master vice = main.clone();

        System.out.println("主对象: " + main);
        System.out.println("副对象: " + vice);
        System.out.println("拷贝后对象的指针是否都指向同一个堆内存: " + (main == vice));
        System.out.println();

        // 修改副对象中的性别属性
        vice.setGender("女");
        System.out.println("主对象: " + main);
        System.out.println("副对象: " + vice);
        System.out.println("修改拷贝对象中的String类型的属性gender后是否相同: " + main.getGender().equals(vice.getGender()));
        System.out.println();


        // 修改副对象中引用对象中的年龄属性
        vice.getPet().setAge(10);
        System.out.println("主对象: " + main);
        System.out.println("副对象: " + vice);
        System.out.println("修改拷贝对象中的引用类型Pet后,是否在同一个堆内存当中: " + (main.getPet() == vice.getPet()));
        System.out.println();
        System.out.println("可以发现,它是对'主'对象的完全拷贝,无论是引用对象还是数组,都会拷贝一个新的对象,并且与源对象的引用完全隔离");
    }
}

// 宠物对象
class Pet implements Cloneable {
    private String name;
    private int age;

    public Pet(String name, int age) {
        this.name = name;
        this.age = age;
    }

	// ...省略set、get方法

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

    // 浅拷贝
    @Override
    public Pet clone() {
        try {
            // TODO: copy mutable state here, so the clone can't change the internals of the original
            return (Pet) super.clone();	// 不同点
        } catch (CloneNotSupportedException e) {
            throw new AssertionError();
        }
    }
}

// 主人对象
class Master implements Cloneable {
    private String gender;

    private Pet pet;

    public Master(String gender) {
        this.gender = gender;
    }

	// ...省略set、get方法

    // 深拷贝
    @Override
    public Master clone() {
        try {
            // TODO: copy mutable state here, so the clone can't change the internals of the original
            Master master = (Master) super.clone();
            master.pet = pet.clone();		// 不同点
            return master;
        } catch (CloneNotSupportedException e) {
            throw new AssertionError();
        }
    }

    @Override
    public String toString() {
        return "Master{" +
                "gender='" + gender + '\'' +
                ", pet=" + pet +
                '}';
    }
}
  • 输出结果

在这里插入图片描述

在这里插入图片描述

三、使用序列化实现深拷贝


什么是序列化? 把对象转换为字节序列的过程称为对象的序列化

什么是反序列化?把字节序列恢复为对象的过程称为对象的反序列化

为什么需要序列化?方便传输、存储,计算机数据传输是以字节为单位的,能处理所有类型的数据

通过序列化实现的拷贝不仅可以复制对象本身,而且可以复制其引用的成员对象,因此通过序列化将对象写到一个流中,再从流里将其读出来,可以实现深拷贝

实现序列化的对象其类必须实现Serializable接口

  • 代码演示
package com.vinjcent;

import java.io.*;

public class Main {

    public static void main(String[] args) {
        // 主人(正)
        Master main = new Master("男");
        // 宠物
        Pet p = new Pet("小狗", 5);

        main.setPet(p);

        // 深拷贝一个"主"对象(副)
        Master vice = main.clone();

        System.out.println("主对象: " + main);
        System.out.println("副对象: " + vice);
        System.out.println("拷贝后对象的指针是否都指向同一个堆内存: " + (main == vice));
        System.out.println();

        // 修改副对象中的性别属性
        vice.setGender("女");
        System.out.println("主对象: " + main);
        System.out.println("副对象: " + vice);
        System.out.println("修改拷贝对象中的String类型的属性gender后是否相同: " + main.getGender().equals(vice.getGender()));
        System.out.println();


        // 修改副对象中引用对象中的年龄属性
        vice.getPet().setAge(10);
        System.out.println("主对象: " + main);
        System.out.println("副对象: " + vice);
        System.out.println("修改拷贝对象中的引用类型Pet后,是否在同一个堆内存当中: " + (main.getPet() == vice.getPet()));
        System.out.println();
        System.out.println("可以发现,它是对'主'对象的序列化实现完全拷贝,无论是引用对象还是数组,都会拷贝一个新的对象,并且与源对象的引用完全隔离");

    }
}

// 宠物对象
class Pet implements Serializable {
    private String name;
    private int age;

    public Pet(String name, int age) {
        this.name = name;
        this.age = age;
    }

	// ...省略set、get方法

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

// 主人对象
class Master implements Serializable {
    private String gender;

    private Pet pet;

    public Master(String gender) {
        this.gender = gender;
    }

	// ...省略set、get方法

    public Master clone() {
        ObjectOutputStream oos;
        ObjectInputStream ois;
        Master master = null;
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        try {
            oos = new ObjectOutputStream(baos);
            oos.writeObject(this);
            // 将流序列化成对象
            ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
            ois = new ObjectInputStream(bais);
            master = (Master) ois.readObject();
        } catch (IOException | ClassNotFoundException e) {
            e.printStackTrace();
        }
        return master;
    }

    @Override
    public String toString() {
        return "Master{" +
                "gender='" + gender + '\'' +
                ", pet=" + pet +
                '}';
    }
}
  • 输出结果

在这里插入图片描述

四、Spring下的BeanUtils.copyProperties()方法进行浅拷贝和深拷贝


对于BeanUtils.copyProperties来说,你必须保证属性名和类型是相同的,因为它是根据get和set方法来赋值的

但,BeanUtils只是浅拷贝

4.1 测试BeanUtils.copyProperties()浅拷贝

  • 代码演示
package com.vinjcent;

import org.springframework.beans.BeanUtils;

public class Main {

    public static void main(String[] args) {
        // 主人(正)
        Master main = new Master("男");
        // 宠物
        Pet p = new Pet("小狗", 5);

        main.setPet(p);

        // 浅拷贝一个"主"对象(副)
        Master vice = new Master();
        BeanUtils.copyProperties(main, vice);   // 修改地方


        System.out.println("主对象: " + main);
        System.out.println("副对象: " + vice);
        System.out.println("拷贝后对象的指针是否都指向同一个堆内存: " + (main == vice));
        System.out.println();

        // 修改副对象中的性别属性
        vice.setGender("女");
        System.out.println("主对象: " + main);
        System.out.println("副对象: " + vice);
        System.out.println("修改拷贝对象中的String类型的属性gender后是否相同: " + main.getGender().equals(vice.getGender()));
        System.out.println();


        // 修改副对象中引用对象中的年龄属性
        vice.getPet().setAge(10);
        System.out.println("主对象: " + main);
        System.out.println("副对象: " + vice);
        System.out.println("修改拷贝对象中的引用类型Pet后,是否在同一个堆内存当中: " + (main.getPet() == vice.getPet()));
        System.out.println();
    }
}

// 宠物对象
class Pet {
    private String name;
    private int age;

    public Pet(String name, int age) {
        this.name = name;
        this.age = age;
    }

	// ...省略set、get方法

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

// 主人对象
class Master {
    private String gender;

    private Pet pet;

    public Master(String gender) {
        this.gender = gender;
    }

    public Master() {}

	// ...省略set、get方法

    @Override
    public String toString() {
        return "Master{" +
                "gender='" + gender + '\'' +
                ", pet=" + pet +
                '}';
    }
}
  • 输出结果

在这里插入图片描述

4.2 测试BeanUtils.copyProperties()深拷贝

从上面提到,我们可以通过序列化的形式来实现bean的深拷贝,可在BeanUtils.copyProperties()方法中如何实现呢?

答:我们可以借助java8的一些stream新特性

  • 代码演示
package com.vinjcent;

import org.springframework.beans.BeanUtils;

import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;

public class Main {

    public static void main(String[] args) {

        // 要是用stream流,一般会使用到集合,先构造一个master集合
        List<Master> main = Arrays.asList(new Master("男"), new Master("女"));
        main = main.stream().map(m -> {
            m.setPet(new Pet("源pet", 5));
            return m;
        }).collect(Collectors.toList());

        // 使用stream流实现bean的深拷贝
        List<Master> vice = main
                .stream()
                .map(m -> {
                    Master temp = new Master();
                    BeanUtils.copyProperties(m, temp);
                    return temp;
                }).collect(Collectors.toList());


        System.out.println("主对象: " + main);
        System.out.println("副对象: " + vice);
        System.out.println("拷贝后对象的指针是否都指向同一个堆内存: " + (main == vice));
        System.out.println();

        // 修改副对象中第一个元素的性别属性
        vice.get(0).setGender("女");
        System.out.println("主对象: " + main);
        System.out.println("副对象: " + vice);
        System.out.println("在拷贝对象中,修改第一个元素的String类型的属性gender后是否相同: " + main.get(0).getGender().equals(vice.get(0).getGender()));
        System.out.println();


        // 修改副对象中第一个元素的引用对象中的年龄、名称属性
        vice.get(0).getPet().setAge(10);
        vice.get(0).getPet().setName("小猫");
        System.out.println("主对象: " + main);
        System.out.println("副对象: " + vice);
        System.out.println("在拷贝对象中,修改第一个元素的引用类型Pet后,是否在同一个堆内存当中: " + (main.get(0).getPet() == vice.get(0).getPet()));
        System.out.println();
        System.out.println("可以发现,它是对'主'对象的拷贝,它不会拷贝'主'对象深层次的可变对象,只做第一层的拷贝");
    }
}

// 宠物对象
class Pet {
    private String name;
    private int age;

    public Pet(String name, int age) {
        this.name = name;
        this.age = age;
    }

	// ...省略set、get方法

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

// 主人对象
class Master {
    private String gender;

    private Pet pet;

    public Master(String gender) {
        this.gender = gender;
    }

    public Master() {
    }

	// ...省略set、get方法

    @Override
    public String toString() {
        return "Master{" +
                "gender='" + gender + '\'' +
                ", pet=" + pet +
                '}';
    }
}
  • 输出结果

在这里插入图片描述

结论由此可知,org.springframework.beans.BeanUtils工具类中的copyProperties()方法无法实现深拷贝,只能实现浅拷贝

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Spring学习笔记(一)【BeanUtils.copyProperties方法】 的相关文章

随机推荐

  • 实现微信小程序web-view内嵌H5中的下载功能(大文件切片下载)

    实现微信小程序内嵌H5中的下载功能 一 项目场景 难点 解决方案 1 H5微信小程序 a 首先必不可少的是安装jweixin module模块 b 在main js中将依赖绑定 c H5对应页面点击下载时代码为 2 uni app的小程序
  • python+selenium+unittest自动化测试

    python selenium unittest自动化测试详解 Base 基类层 最基础类 供其他文件调用 封装浏览器方法 以方便后面代码的调用 可以二次封装自带的方法 方便调用 其他地方调用基础类的方法 如果需要修改调用地方的方法 只需要
  • 为什么我们要用Spring?

    在之前的文章说说java反射 2 反射的价值我们对java反射的内容有了一定的了解 并且通过反射机制做了一个简单的框架 也算是对于Spring框架的铺垫 接下来了解一下我们使用Spring框架的意义 Spring是一个解决了许多在J2EE开
  • 始料未及的COVID-19、延期的考试、又可以瞎倒腾的喜悦

    Hmmm 真的本来下定决心不要东摸西摸专心功课 但是确实发生了本次人类历史上的大事件 当然可能在以后的日子里这个也就是洒洒水水平 导致了考试的延期 我又再一次的东张西望起来 hmmm 好吧为了庆祝又可以自由活动 特此献上抄来的小程序一篇 以
  • 解决Rosbridge自定义action信息问题

    前言 利用rosbridge开发一个网页版的action客户端 一 rosbridge原理 rosbridge协议 该协议的基本思想是将节点间的分布式通信 改成 client节点 与一个代理节点进行C S通信 然后代理节点再将请求转发给se
  • 多服务环境下定时任务重复执行问题解决方案

    当一个服务部署在多台服务器上时 定时任务可能出现多次执行的情况 就是每个服务上执行一次 有以下两种思路 一是固定死只有某服务器执行定时任务 二是随机暂停几秒 某一服务执行了 其他就不再执行 1 固定某一个服务器作为执行定时任务的机器 通过配
  • 基于混沌映射与差分进化的自适应教与学优化算法

    文章目录 一 理论基础 1 标准TLBO算法 2 混沌映射与差分进化的自适应TLBO算法 1 改进的Logistic混沌映射 2 惯性权重自适应调节函数 3 教改阶段 二 仿真实验与分析 三 参考文献 一 理论基础 1 标准TLBO算法 请
  • 在Power BI中用DAX新建列的方式进行累计求和

    在Power BI中用DAX新建列的方式进行累计求和 DAX 新建列来累计求和 累计求和 DAX 新建列 DAX函数 Filter DAX函数 EARLIER DAX函数 SUMX DAX 新建列来累计求和 Power BI有两种方式进行累
  • 没有node-sass Windows 64-bit with Node.js 16.x

    throw new Error errors missingBinary OS X 64 bit with Node js 16 x Windows 64 bit with Node js 16 x 1 下载node sass 对应的版本
  • 数据分析理论【5】之 下采样策略和上采样策略

    合辑传送门 gt gt 数据分析 合辑 在分类问题的数据中 很容易出现正反数据集数量存在极大的差距 这类数据直接用于训练不利于模型的构架 所以我们需要对数据进行些许处理 很容易想到 合理的数据集应该是正反数据集数量应接近 那就存在两种策略
  • python的艰难学习之路-综合练习--名片管理系统

    名片管理系统需求 1 显示欢迎界面 提示操作码 按照操作码执行程序 2 操作码功能包括 查询 查找 新增 3 查询出来后 可继续操作 gt 修改 删除 4 名片需要记录的信息 gt 编号 姓名 电话 邮箱 QQ 5 编号自动生成 6 如果在
  • 通过高德地图API取得两点坐标间的距离

    高德地图在取两点间的距离比百度地图更详细 可以分为几种类型的API 提供的步行 公交 驾车查询 今天咱们使用驾车的API来计算两点的距离 其它的API大同小异 参考高德地图的API地址 路径规划 API文档 开发指南 Web服务 API 高
  • RunBlocking CoroutineScope SupervisorScope Launch Async CoroutineStart协程启动模式 Job对象和生命周期

    协程的作用域构建器 RunBlocking runBlocking是常规函数 会把当前主线程包装成一个主协程 其会阻塞当前线程 只有当等待其主协程体以及里面的所有子协程执行结束以后 才会让当前线程执行 CoroutineScope coro
  • QT foreach

    如果只想按顺序迭代容器中的所有项 可以使用Qt的foreach关键字 该关键字是对C 语言的Qt特定添加 并使用预处理器实现 与任何其他C 循环构造一样 您可以在foreach循环的主体周围使用大括号 并且可以使用Break来离开循环 其语
  • 华为OD机试 - 数组连续和(Java)

    题目描述 给定一个含有N个正整数的数组 求出有多少个连续区间 包括单个正整数 它们的和大于等于x 输入描述 第一行两个整数N x 0 lt N lt 100000 0 lt x lt 10000000 第二行有N个正整数 每个正整数小于等于
  • 锐捷交换机密码破解

    资料来源 https search ruijie com cn 8447 rqs preview html ie utf 8 wd eHAiOjE1NDU4NzUxNDcsIm5iZiI6MTU0NTYxNTk0N3020180920150
  • 虚拟机-扩充硬盘

    扩充硬盘 https www cnblogs com wy20110919 p 9150914 html https cloud tencent com developer article 1563508 from 14588
  • next_permutation(a,a+n)

    早就听说了了next permutation 产生全排列的强大 一直到昨晚遇到一个对字符串产生全排列的问题才知道这个函数的强大 我们队是按照dfs去搞全排列 然后在进行字符串的匹配 结果写的很长 过程中还各种debug 于是决定今天学一下
  • 认知-想象力:想象力

    ylbtech 认知 想象力 想象力 想象力 是人在已有形象的基础上 在头脑中创造出新形象的能力 比如当你说起汽车 我马上就想像出各种各样的汽车形象来就是这个 道理 因此 想象一般是在掌握一定的知识面的基础上完成的 想象力 是在你头脑中创造
  • Spring学习笔记(一)【BeanUtils.copyProperties方法】

    Spring下的BeanUtils copyProperties方法是深拷贝还是浅拷贝 一 浅拷贝深拷贝的理解 简单地说 拷贝就是将一个类中的属性拷贝到另一个中 对于BeanUtils copyProperties来说 必须保证属性名和类型