又踩坑了!BeanUtils.copyProperties浅拷贝的坑

发布时间:2025-05-17 11:23:49 作者:益强科技 来源:undefined 浏览量(1) 点赞(1)
摘要:来源:捡田螺的小男孩 前言 大家好,我是田螺。 你知道什么是深拷贝、什么是浅拷贝嘛?相信以前面试,不少面试官都有问过这个问题。你踩过浅拷贝的坑吗?今天田螺哥跟大家聊聊浅拷贝的一个坑,BeanUtils.copyProperties。

来源:捡田螺的小男孩

前言

大家好,我是田螺

你知道什么是深拷贝、什么是浅拷贝嘛?相信以前面试,不少面试官都有问过这个问题。你踩过浅拷贝的坑吗?今天田螺哥跟大家聊聊浅拷贝的一个坑,BeanUtils.copyProperties。

1. 什么是深拷贝?什么是浅拷贝

浅拷贝 是指创建一个新对象,该对象的属性值与原始对象相同,但对于引用类型的属性,仍然共享相同的引用。换句话说,浅拷贝只复制对象及其引用,而不复制引用指向的对象本身。深拷贝是指创建一个新对象,该对象的属性值与原始对象相同,包括引用类型的属性。深拷贝会递归复制引用对象,创建全新的对象,以确保拷贝后的对象与原始对象完全独立。

2. 一个浅拷贝的例子

我们来定义两个类:

@Data

@AllArgsConstructor

@NoArgsConstructor

public class Address {

   private String province;

   private String city;

}

@Data

@AllArgsConstructor

@NoArgsConstructor

public class Order {

    //订单ID

    private String orderId;

    //收货地址

    private Address shippingAddress;

}

我们写一段代码测试一下:

public class Test {

    public static void main(String[] args) {

        Address oldAddress = new Address("广东""湛江"

);

        Order oldOrder = new Order("666"

, oldAddress);

        Order newOrder = new Order();

        BeanUtils.copyProperties(oldOrder, newOrder);

        System.out.println(oldOrder.getShippingAddress() == newOrder.getShippingAddress());

        oldOrder.getShippingAddress().setCity("深圳"

);

        System.out.println(JSON.toJSONString(oldOrder));

        System.out.println(JSON.toJSONString(newOrder));

    }

}

//输出

true{"orderId":"666","shippingAddress":{"city":"深圳","province":"广东"

}}

{"orderId":"666","shippingAddress":{"city":"深圳","province":"广东"

}}

可以发现,使用了BeanUtils.copyProperties之后呢,新订单和老订单对象的地址属性,指向同一个引用,也就是说,BeanUtils.copyProperties是浅拷贝。

当我们对oldOrder的shippingAddress修改时,newOrder的shippingAddress也会同时被修改。

我们日常开发的时候,经常使用BeanUtils.copyProperties,这时候尤其要注意,这个浅拷贝的坑,要不然某个引用属性被莫名奇妙修改了都不知道。

3. 如何避免浅拷贝的坑

很多时候,我们需要用到深拷贝。如何实现呢?

主要有这几种:

手动new 创建实现Cloneable接口,重写clone()序列化实现深拷贝MapStruct深拷贝写法

3.1 手动new 创建

实现深拷贝, 如果它的属性是一个引用对象类型,最简单直接的方式,就是直接给它new一个。

我们修改一下之前的代码例子,加个deepCopy的方法,如下:

@Data

@AllArgsConstructor

@NoArgsConstructor

public class Order {

    private String orderId;

    private Address shippingAddress;

    public Order deepCopy

() {

        //深拷贝、新new一个地址对象出来

        Address newAddress = new Address(this.shippingAddress.getProvince(), this.shippingAddress.getCity());

        return

 new Order(this.orderId, newAddress);

    }

}

再次验证一下:

  public static void main(String[] args) {

        Address oldAddress = new Address("广东""湛江"

);

        Order oldOrder = new Order("666"

, oldAddress);

        Order newOrder = oldOrder.deepCopy();

        System.out.println(oldOrder.getShippingAddress() == newOrder.getShippingAddress());

        oldOrder.getShippingAddress().setCity("深圳"

);

        System.out.println(JSON.toJSONString(oldOrder));

        System.out.println(JSON.toJSONString(newOrder));

    }

//输出

false{"orderId":"666","shippingAddress":{"city":"深圳","province":"广东"

}}

{"orderId":"666","shippingAddress":{"city":"湛江","province":"广东"

}}

3.2 实现Cloneable接口,重写clone()

Object类中有个clone()的方法,如果不重写它的话·,实现的是浅拷贝。

@Data

@AllArgsConstructor

@NoArgsConstructor

public class Address implements Cloneable{

    private String province;

    private String city;

    //重写clone

    @Override

    protected Object clone

() throws CloneNotSupportedException {

        return

 super.clone();

    }

}

@Data

@AllArgsConstructor

@NoArgsConstructor

public class Order implements Cloneable {

    private String orderId;

    private Address shippingAddress;

    //重写clone

    @Override

    protected Object clone

() throws CloneNotSupportedException {

        Order newOrder = (Order)super.clone();

        newOrder.setShippingAddress((Address)shippingAddress.clone());

        return

 newOrder;

    }

}

3.3 序列化实现深拷贝

我们还可以使用SerializationUtils.clone ,也就是序列化实现深拷贝。

@Data

@AllArgsConstructor

@NoArgsConstructor

public class Order implements Serializable {

    private String orderId;

    private Address shippingAddress;

    public  Order deepCopy(Order order) {

        //序列化实现深拷贝

        return

 SerializationUtils.clone(order);

    }

}

但是,SerializationUtils.clone相对于其他,性能方面不是很理想,大家可以自己去验证一下看看哈。

3.4 MapStruct深拷贝写法

MapStruct 不直接支持深拷贝,但你可以自定义映射方法来实现类似深拷贝的效果。

@Mapper

public interface AddressMapper {

    Address deepCopy(Address address);

}

@Mapper(uses = {AddressMapper.class})

public interface OrderMapper {

    OrderMapper INSTANCE = Mappers.getMapper(OrderMapper.class);

    @Mapping(target = "shippingAddress"source = "order.shippingAddress"

)

    Order deepCopy(Order order);

}

@Data

@AllArgsConstructor

@NoArgsConstructor

public class Order implements Serializable {

    private String orderId;

    private Address shippingAddress;

    //MapStruct写法的深拷贝

    public Order deepCopy

() {

        OrderMapper mapper = OrderMapper.INSTANCE;

        return

 mapper.deepCopy(this);

    }

}

我是田螺,下期我们不见不散~

二维码

扫一扫,关注我们

声明:本文由【益强科技】编辑上传发布,转载此文章须经作者同意,并请附上出处【益强科技】及本页链接。如内容、图片有任何版权问题,请联系我们进行处理。

感兴趣吗?

欢迎联系我们,我们愿意为您解答任何有关网站疑难问题!

您身边的【网站建设专家】

搜索千万次不如咨询1次

主营项目:网站建设,手机网站,响应式网站,SEO优化,小程序开发,公众号系统,软件开发等

立即咨询 15687827356
在线客服
嘿,我来帮您!