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

news2024/11/19 1:49:46

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()方法无法实现深拷贝,只能实现浅拷贝

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/183792.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Windows下安装Python和配置easygui

一、需求说明需要学习Python内容&#xff0c;或者是运行Python程序&#xff0c;需要在Windows系统下进行安装配置Python的相关环境&#xff0c;以便让程序能够正常运行使用。二、安装Python2.1、下载Python安装包打开Python官网下载自己Windows系统需要的Python版本选择【Downl…

大宇无限将全部业务系统都部署在亚马逊云科技上

随着移动互联网的高速发展&#xff0c;人们对精神文化内容消费的追求不断提高&#xff0c;利用互联网技术&#xff0c;便捷地享受资讯内容成为了当代人的诉求。为了实现这个目标&#xff0c;大宇无限面临的首要挑战是如何利用大数据系统高效地分析数据、了解用户的消费行为和国…

1、数据库安装超详细教程(MySql5.0版本)

1.1、SQL概述 SQL&#xff0c;一般发音为sequel&#xff0c;SQL的全称Structured Query Language)&#xff0c;SQL用来和数据库打交道&#xff0c;完成和数据库的通信&#xff0c;SQL是一套标准。但是每一个数据库都有自己的特性别的数据库没有,当使用这个数据库特性相关的功能…

Quarkus入门体验,22ms启动一个Web服务

简介 Quarkus是类似于Spring Boot的框架&#xff0c;可以方便大家进行Java开发。利用GraalVM的魔力&#xff0c;能更好的适应云原生的场景&#xff0c;极快的启动速度。 创建项目 在IDEA就直接有创建Quarkus项目的初始化工具&#xff0c;直接根据自己需要填好即可&#xff0…

分享一个可以看历史影像的网站

概述 众所周知&#xff0c;由于某些原因&#xff0c;大家以前经常用的历史地图已经看不了了&#xff0c;那么是否有替代的资源呢&#xff0c;答案是肯定的&#xff0c;而且这个网站的所有者大家都非常的熟悉——Esri&#xff0c;该网站名为World Imagery Wayback&#xff0c;这…

【模型↔关系思考法】如何在一个全新的、陌生的领域快速成为专家?模仿 + 一万小时定律 + 创新...

核心基础:形成模型 行业特性,行业名词术语(模型),行业前规则,行业风险 首先搞懂行业内独有的一些专业名词术语(建立“模型”)的含义,这些是基础。 比如餐饮行业的翻台率、开台率,电商的GMV、SKU、SPU; 通过互联网文章,网站,了解行业的趋势和动向、竞品分析等。 了…

NSS_Round#7部分wp

Web ec_RCE 源码: <!-- A EZ RCE IN REALWORLD _ FROM CHINA.TW --> <!-- By 探姬 --> <?PHPif(!isset($_POST["action"]) && !isset($_POST["data"]))show_source(__FILE__);putenv(LANGzh_TW.utf8); $action $_POST["a…

Jenkins入门(二)Jenkins安装及自动构建部署

一、安装环境 1. 机器要求&#xff1a; 256 MB 内存&#xff0c;建议大于 512 MB 10 GB 的硬盘空间&#xff08;用于 Jenkins 和 Docker 镜像&#xff09; 需要安装以下软件&#xff1a; Java 8 ( JRE 或者 JDK 都可以) 2. 安装JDK 检索可用包 yum search java|grep jd…

Java之JDBC-ResultSet(结果集)

之前的dml语句都返回值都是int表示有改动的行数 那么Select可是要展示数据的 SELECT查询的结果 通过这个ResultSet遍历 然后通过next方法来一行行读取数据 类似于迭代器(肯定不是迭代器) 里面还包含获得的数据元素 相当于这个既包含元素&#xff0c;还能迭代自己的元素 具体你…

[Vulnhub] DC-5

下载链接&#xff1a;https://download.vulnhub.com/dc/DC-5.zip 同DC-4 这个靶机也是只有一个flag 不过官方描述说这个可能提高了一点点难度 官方描述&#xff1a; 据我所知&#xff0c;只有一个可利用的入口点可以进入&#xff08;也没有 SSH&#xff09;。这个特定的入口点…

RocketMQ 原理介绍及详细安装教程

一、为什么选择RocketMQ Apache RocketMQ 自诞生以来&#xff0c;因其架构简单、业务功能丰富、具备极强可扩展性等特点被众多企业开发者以及云厂商广泛采用。历经十余年的大规模场景打磨&#xff0c;RocketMQ 已经成为业内共识的金融级可靠业务消息首选方案&#xff0c;被广泛…

python使用pywin32库将ppt导出为高清图片

python使用pywin32库将ppt导出为高清图片 作者&#xff1a;虚坏叔叔 博客&#xff1a;https://xuhss.com 早餐店不会开到晚上&#xff0c;想吃的人早就来了&#xff01;&#x1f604; 一、安装库 需要安装pywin32库 pip install pywin32二、代码原理 WPS高清图片导出需要会…

Fisco Bcos区块链四(WeBase结点前置服务)

文章目录区块链开荒技术文档&#xff1a;https://fisco-bcos-documentation.readthedocs.io/zh_CN/latest/index.html5、WeBASE节点前置服务1、前提条件2、拉取代码3. 编译代码4. 修改配置&#xff08;1&#xff09;进入dist目录&#xff08;2&#xff09;进入conf目录复制配置…

一些常见代码如from __future__ import ...的功能

1. 有一些代码在一开始会通过from __future__ import...导入模块。 __future__是什么&#xff1f; 随着python版本的升级&#xff0c;一些功能也会产生变化&#xff0c;__future__中包括了一些新版本的功能&#xff0c;通过导入__future__中的模块&#xff0c;可以在旧pyth…

IPSec

IPSec是网际层实现IP分组端到端安全传输的机制&#xff0c;由一组安全协议组成。 鉴别首部&#xff08;Authentication Header&#xff0c;AH&#xff09;和封装安全净荷&#xff08;Encapsulating Security Payload&#xff0c;ESP&#xff09;是其中两个协议&#xff0c;AH和…

如何云同步vscode的配置到码云(gitee)上

目录 &#x1f525; 写在前面 一、安装插件 二、创建Gist &#x1f449; 如何获取Gist ID &#x1f449; 如何进入代码片段管理页面 三、创建私人令牌 四、插件配置 五、插件使用 &#x1f449; 上传配置 &#xff1a;命令为 upload setting &#x1f449; 下载配置 …

基于语义分割Ground Truth(GT)转换yolov5图像分割标签(路面积水检测例子)

基于语义分割Ground Truth&#xff08;GT&#xff09;转换yolov5图像分割标签&#xff08;路面积水检测例子&#xff09; 概述 随着开发者在issues中对 用yolov5做分割任务的呼声高涨&#xff0c;yolov5团队真的在帮开发者解决问题&#xff0c;v6.0版本之后推出了最新的解决方…

2、等价类划分

如何划分等价类 在给定了输入或外部条件之后&#xff0c;等价类的划分原则如下&#xff1a; 如果输入条件规定了一个取值范围&#xff08;例如&#xff0c;“数量可以是1到999”&#xff09;&#xff0c;那么就应确定出一个有效等价类&#xff08;1<数量<999&#xff0…

【大唐杯备考】——5G网络架构的演进趋势(学习笔记)

&#x1f4d6; 前言&#xff1a;本期介绍5G网络架构的演进趋势。 目录&#x1f552; 1. 5G网络架构概述&#x1f552; 2. 4G网络架构概述&#x1f552; 3. 核心网架构演进&#xff08;了解&#xff09;&#x1f558; 3.1 2G核心网&#x1f558; 3.2 3G、4G核心网&#x1f558; …

Golang - 如何编写Go代码

Golang - 如何编写Go代码 今天学习下如何在Go模块中开发一个简单的Package包&#xff0c;并介绍Go相关工具&#xff0c;以及如何获取、构建、安装模块等使用方法。本文中的案例需要使用Go 1.13及以上版本。 代码组织 Go程序以package包的形式组织&#xff0c;package包是同一…