您的位置:

Java深拷贝的实现方法

一、基础概念

深拷贝和浅拷贝是Java中的两个重要的概念。通过这两种方式,我们可以实现对象的复制和传递。

深拷贝是指创建一个新的对象,同时将原对象中引用的其他对象也复制一份。换句话说,深拷贝是对整个对象进行复制,包括对象内部的引用类型成员变量。

而浅拷贝只是将原对象的引用复制一份,即对于引用成员变量,只复制了引用本身,而没有复制引用指向的对象。因此,浅拷贝会导致多个对象共享同一个引用,从而可能导致数据意外修改。

二、Java中的深拷贝实现方法

1.序列化与反序列化

Java中实现深拷贝最常用的方法就是通过序列化与反序列化。这种方式适用于对象中没有引用类型的成员变量的情况。

具体实现方法如下:

public static  T deepCopy(T obj) throws Exception {
    ByteArrayOutputStream bos = new ByteArrayOutputStream();
    ObjectOutputStream oos = new ObjectOutputStream(bos);
    oos.writeObject(obj);
    ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray());
    ObjectInputStream ois = new ObjectInputStream(bis);
    return (T) ois.readObject();
}

  

该方法首先将对象序列化成字节数组,再通过反序列化的方式,将字节数组反序列化成新的对象。由于序列化时会将整个对象图进行遍历,因此可以实现深拷贝。

需要注意的是,要实现Java对象的序列化和反序列化,其类必须实现Serializable接口。

2.递归拷贝

递归拷贝是另一种实现Java深拷贝的方法。该方法适用于对象中存在引用类型的成员变量的情况。

具体实现方法如下:

public static  T deepCopy(T obj) throws Exception{
    if(obj == null){
        return null;
    }
    Class clazz = obj.getClass();
    T copyObj = null;
    //如果需要拷贝的是基本类型或字符串,直接返回该对象
    if(clazz.isPrimitive() || obj instanceof String){
        copyObj = obj;
    }else if(obj instanceof Collection){//拷贝集合类型
        Collection collection = (Collection) obj;
        //判断是List类型还是Set类型
        if(obj instanceof List){
            List list = (List) obj;
            List copyList = new ArrayList(list.size());
            for(Object item : list){
                copyList.add(deepCopy(item));
            }
            copyObj = (T) copyList;
        }else if(obj instanceof Set){
            Set set = (Set) obj;
            Set copySet = new HashSet();
            for(Object item : set){
                copySet.add(deepCopy(item));
            }
            copyObj = (T) copySet;
        }
    }else if(obj instanceof Map){//拷贝Map类型
        Map map = (Map) obj;
        Map copyMap = new HashMap(map.size());
        for(Map.Entry entry : map.entrySet()){
            copyMap.put(deepCopy(entry.getKey()), deepCopy(entry.getValue()));
        }
        copyObj = (T) copyMap;
    }else {//拷贝自定义对象类型
        copyObj = (T) clazz.newInstance();
        Field[] fields = clazz.getDeclaredFields();
        for(Field field : fields){
            field.setAccessible(true);
            //递归拷贝引用对象
            field.set(copyObj, deepCopy(field.get(obj)));
        }
    }
    return copyObj;
}

  

该方法首先判断传入对象是不是基本数据类型或字符串类型,如果是,直接返回该对象。如果是集合类型(List、Set、Map),则递归处理其中的每个元素。如果是自定义对象类型,则通过反射将对象中的每个成员变量都拷贝一份,并递归拷贝其中的引用类型成员变量。

3.使用clone方法

在Java中,对象的clone方法可以用于实现浅拷贝,而通过重写clone方法,也可以实现深拷贝。

具体实现方法如下:

public class Person implements Cloneable {
    private String name;
    private Address address;

    //省略getter/setter
    //重写clone方法
    @Override
    public Object clone() throws CloneNotSupportedException {
        Person newPerson = (Person) super.clone();
        newPerson.address = (Address) this.address.clone();
        return newPerson;
    }
}

public class Address implements Cloneable {
    private String city;
    private String district;

    //省略getter/setter
    //重写clone方法
    @Override
    public Object clone() throws CloneNotSupportedException {
        return super.clone();
    }
}

//测试代码
public void testClone() throws CloneNotSupportedException {
    Address address = new Address();
    address.setCity("Beijing");
    address.setDistrict("Haidian");
    Person person1 = new Person();
    person1.setName("Tom");
    person1.setAddress(address);
    //通过clone方法实现深拷贝
    Person person2 = (Person) person1.clone();
    Assert.assertNotSame(person1, person2);
    Assert.assertNotSame(person1.getAddress(), person2.getAddress());
}

在上面的代码中,Person类和Address类都重写了clone方法,并在clone方法中实现了深拷贝。在测试代码中,通过调用person1的clone方法,即可实现person1的深拷贝,生成一个新的对象person2。需要注意的是,Person类和Address类都必须实现Cloneable接口才能进行深拷贝。

三、深拷贝的应用场景

深拷贝可以应用于很多场景,例如:

  • 多线程数据共享。由于多线程之间的数据彼此独立,因此每个线程都需要拥有自己的数据副本,以免多个线程同时读写同一个数据造成意外修改。
  • 原型模式(Prototype Pattern)。当我们需要使用一个对象作为模板,从而创建多个类似的对象时,深拷贝可以帮助我们快速创建新的对象副本。
  • 对象传递。当我们需要将一个对象作为参数传递给另一个方法或类时,为了保证原对象数据的安全性,我们可以使用深拷贝。

四、总结

Java中的深拷贝可以通过序列化与反序列化、递归拷贝、重写clone方法等多种方式实现。在实际应用中,我们需要根据具体情况选择最适合的深拷贝实现方法。深拷贝可以应用于多线程数据共享、原型模式、对象传递等场景,为代码开发提供了便利。