Java深拷贝的实现

 2022-09-16
原文地址:https://blog.csdn.net/hudeyong926/article/details/114675276

浅拷贝

如果一个类中有指针对象,那么在拷贝这个类的对象的时候,默认的拷贝方式是只拷贝指针本身,而不重新构建并拷贝指针所指内容。这就叫做浅拷贝Shallow Copy。Java的 Object.clone() 的机制是浅拷贝

深拷贝

如果拷贝的方式是不仅仅拷贝指针,而且把指针所指的内容也新建一份,那就叫深拷贝Deep Copy。

在有些业务场景下,我们需要两个完全相同却彼此无关的java对象。比如使用原型模式、多线程编程等。对此,java提供了深拷贝的概念。通过深度拷贝可以从源对象完美复制出一个相同却与源对象彼此独立的目标对象。这里的相同是指两个对象的状态和动作相同,彼此独立是指改变其中一个对象的状态不会影响到另外一个对象。实现深拷贝常用的实现方式有2种:Serializable,Cloneable。工作中遇到的大多是Serializable方式,这种方式代码量小,不容易出错。

    public class CloneUtils {
    
    	public static <T extends Serializable> T clone(T obj) {
    		T cloneObj = null;
    		try {
    			//写入字节流
    			ByteArrayOutputStream out = new ByteArrayOutputStream();
    			ObjectOutputStream obs = new ObjectOutputStream(out);
    			obs.writeObject(obj);
    			obs.close();
    
    			//分配内存,写入原始对象,生成新对象
    			ByteArrayInputStream is = new ByteArrayInputStream(out.toByteArray());
    			ObjectInputStream os = new ObjectInputStream(is);
    			cloneObj = (T) os.readObject();
    			os.close();
    		} catch (Exception e) {
    			e.printStackTrace();
    		}
    		return cloneObj;
    	}
    }

使用方法

    BattleShip bs = new BattleShip("Dominix", new ClonePilot("Alex", "male"));
    System.out.println(bs);
    System.out.println(bs.name + " "+bs.pilot.name);
    BattleShip cloneBs = (BattleShip) CloneUtils.clone(bs);
    System.out.println(cloneBs);
    System.out.println(cloneBs.name + " "+cloneBs.pilot.name);

BeanUtils.copyProperties()

Spring包下的org.springframework.beans.BeanUtils.copyProperties();

    public static void copyProperties(Object source, Object target) throws BeansException {
        copyProperties(source, target, null, (String[]) null);
    }

Apeche包下的org.apache.commons.beanutils.BeanUtils.copyProperties();

    public static void copyProperties(Object dest, Object orig)
            throws IllegalAccessException, InvocationTargetException {
    
        BeanUtilsBean.getInstance().copyProperties(dest, orig);
    }

通过源码可以发现,Spring包下的原类在前,目标类在后;而Apeche包下的恰恰相反。

三.总结

1.看了下源码和网上讨论,Spring的BeanUtils.copyProperties()会忽略null的属性,只拷贝有值的属性,相比Apache包下的不容易出现一些错误。

  • source对象和target对象相应属性的名称和类型必须都一样才可以成功拷贝属性值
  • source对象和target对象的属性增加set、get方法。比如给name、age属性增加set、get方法

2.无论Spring的还是Apache的,cglib包下也有一个对象拷贝工具,Spring和Apache是使用反射机制实现的,cglib是使用动态代理实现的。它们都会牺牲一定的效率。

异常兼容

    User user= userService.getList(feeId, phone, productType, today);
    if (Objects.isNull(user)) {
        user = new User();
        UserVO vo = new UserVO();
        BeanUtils.copyProperties(user, vo);
    }

忽略某个属性拷贝

    BeanUtils.copyProperties(kafkaNoticeVO,kafkaNotice,new String[]{"actiontime","dbtime"})