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

这篇具有很好参考价值的文章主要介绍了Spring学习笔记(一)【BeanUtils.copyProperties方法】。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

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

一、浅拷贝深拷贝的理解


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

1.1、浅拷贝

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

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

1.2、深拷贝

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

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

beanutils.copyproperties,Spring,spring,学习,java

二、测试浅拷贝与深拷贝


  • 浅拷贝
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 +
                '}';
    }
}
  • 输出结果

beanutils.copyproperties,Spring,spring,学习,java

beanutils.copyproperties,Spring,spring,学习,java

  • 深拷贝
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 +
                '}';
    }
}
  • 输出结果

beanutils.copyproperties,Spring,spring,学习,java

beanutils.copyproperties,Spring,spring,学习,java

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


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

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

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

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

实现序列化的对象其类必须实现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 +
                '}';
    }
}
  • 输出结果

beanutils.copyproperties,Spring,spring,学习,java

四、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 +
                '}';
    }
}
  • 输出结果

beanutils.copyproperties,Spring,spring,学习,java

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 +
                '}';
    }
}
  • 输出结果

beanutils.copyproperties,Spring,spring,学习,java

结论由此可知,org.springframework.beans.BeanUtils工具类中的copyProperties()方法无法实现深拷贝,只能实现浅拷贝文章来源地址https://www.toymoban.com/news/detail-794487.html

到了这里,关于Spring学习笔记(一)【BeanUtils.copyProperties方法】的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 是时候丢掉BeanUtils了

    为了更好的进行开发和维护,我们都会对程序进行分层设计,例如常见的三层,四层,每层各司其职,相互配合。也随着分层,出现了VO,BO,PO,DTO,每层都会处理自己的数据对象,然后向上传递,这就避免不了经常要将一个对象的属性拷贝给另一个对象。 例如我有一个Us

    2024年02月15日
    浏览(24)
  • 解决 BeanUtil.copyProperties 不同属性直接的复制

    1、引入hutool hutool官网 2、直接上例子  对象:User.java 对象:User1.java  主要关注 @Alias 注解。引入的时候需要主题引入的是hutool包的。引入其它包的不行 复制业务代码 执行结果

    2024年01月24日
    浏览(32)
  • 告别BeanUtils,Mapstruct从入门到精通

    如果你现在还在使用BeanUtils,看了本文,也会像我一样,从此改用Mapstruct。 对象之间的属性拷贝,之前用的是Spring的BeanUtils,有一次,在学习领域驱动设计的时候,看了一位大佬的文章,他在文章中提到使用Mapstruct做DO和Entity的相互转换,出于好奇,后来就去了解了一下Map

    2024年02月10日
    浏览(28)
  • 麻了,不要再动不动就BeanUtil.copyProperties!!!

    最近项目上要求升级一个工具包 hutool 的版本,以解决安全漏洞问题,这不升级还好,一升级反而捅出了更大的篓子,究竟是怎么回事呢? 我们项目原先使用的 hutool 版本是5.7.2,在代码中,我们的数据传输对象DTO和数据实体对象中大量使用了工具包中的 BeanUtil.copyProperties()

    2023年04月16日
    浏览(72)
  • hutool的BeanUtil.copyProperties复制枚举类属性大坑

            项目中需要使用到对象属性复制,于是使用hutool的BeanUtil.copyProperties方法。这个方法线上一直用着都没问题,然而最近修改代码后却突然报错: Can not convert XXX to  XXX。 结合代码得知,该报错为把Map中的字符串复制到Bean的枚举类属性,并为该属性设置对应对象时出现

    2024年02月05日
    浏览(46)
  • 【开发新的】apache common BeanUtils忽略null值

    前言: BeanUtils默认的populate方法不会忽略空值和null值,在特定场景,我们需要原始的值避免被覆盖,所以这里提供一种自定义实现方式。 原版实现:

    2024年02月06日
    浏览(34)
  • Jakarta 的 Servlet 下BeanUtils的日期处理 和JSTL 的使用

    jsp优于性能等问题已经不被spring boot等支持,如果想使用jsp和jstl标签库需要引入一下依赖。 org.apache下的BeanUtils工具对表单的处理比较简单,但是不能处理日期类型,需要引入下面的代码进行转换:

    2024年02月12日
    浏览(42)
  • 父类对象的属性直接赋值给子类对象(使用copyProperties中的方法copyProperties)

    BeanUtils.copyProperties() 是 Apache Commons BeanUtils 包中提供的一个方法,用于将一个 JavaBean 对象的属性值赋值到另一个 JavaBean 对象中。该方法可以简化 JavaBean 之间的属性复制过程,避免手动编写大量的赋值代码。 以下是 BeanUtils.copyProperties() 方法的基本用法: 在上面的例子中,首

    2024年02月15日
    浏览(42)
  • Spring学习笔记+SpringMvc+SpringBoot学习笔记

    1.1 概念 1、 POJO 是 Plain Old Java Object(简单老式Java对象)的缩写。它是指在Java开发中普通的Java对象,不依赖于特定的框架或技术。POJO 类型通常用于表示领域模型、数据传输对象(DTO)或实体对象等。 1.2 注解 1.1 SpringMVC概述 SpringMVC用于表现层开发,与Servlet相似,但使用上比

    2024年02月12日
    浏览(40)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包