JAVA 对象的深拷贝

package com.ctrip.ibu.itinerary.common.util;

import com.google.common.primitives.Primitives;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import sun.misc.Unsafe;

import java.lang.reflect.Array;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.util.IdentityHashMap;
import java.util.Map;

/**
 * The utility to deep copy the given object.
 */
public final class CopyUtils {
    private static final Logger log = LoggerFactory.getLogger(CopyUtils.class);

    private CopyUtils() {
    }

    /**
     * Deep-copy the given object.
     *
     * @param src the source object to be deep-copied.
     * @param <T> the type of the given object.
     * @return the deep-copied object.
     * @throws IllegalAccessException
     * @throws NoSuchFieldException
     * @throws InstantiationException
     */
    public static <T> T copy(T src) throws IllegalAccessException, NoSuchFieldException, InstantiationException {
        return doCopy(src, new IdentityHashMap<>());
    }

    /**
     * Deep-copy the given object.
     *
     * @param src     the source object to be deep-copied.
     * @param visited the copied object collection.
     * @param <T>     the type of the given object.
     * @return the deep-copied object.
     * @throws IllegalAccessException
     * @throws NoSuchFieldException
     * @throws InstantiationException
     */
    private static <T> T doCopy(T src, Map<Object, Object> visited) throws IllegalAccessException, NoSuchFieldException, InstantiationException {
        if (src == null) {
            return null;
        }
        if (src.getClass().isAssignableFrom(String.class)
                || src.getClass().isEnum()
                || src.getClass() == Class.class) {
            return src;
        }
        if (visited.containsKey(src)) {
            return (T) visited.get(src);
        }
        if (src.getClass().isArray()) {
            return copyArray(src, visited);
        }
        return copyObject(src, visited);
    }

    /**
     * Gets the unsafe object to create a new instance.
     *
     * @return the {@link Unsafe} object.
     * @throws NoSuchFieldException
     * @throws IllegalAccessException
     */
    private static Unsafe getUnsafe() throws NoSuchFieldException, IllegalAccessException {
        Field f = Unsafe.class.getDeclaredField("theUnsafe");
        f.setAccessible(true);
        return (Unsafe) f.get(null);
    }

    /**
     * Deep-copy the array object.
     *
     * @param src     the given object with array type.
     * @param visited the copied object collection.
     * @param <T>     the type of the element in the given array.
     * @return the deep-copied array object.
     * @throws IllegalAccessException
     * @throws NoSuchFieldException
     * @throws InstantiationException
     */
    private static <T> T copyArray(T src, Map<Object, Object> visited) throws IllegalAccessException, NoSuchFieldException, InstantiationException {
        int length = Array.getLength(src);
        Object result =
                Array.newInstance(
                        src.getClass().getComponentType(),
                        Array.getLength(src));
        visited.put(src, result);
        for (int i = 0; i < length; i++) {
            Array.set(result, i, doCopy(Array.get(src, i), visited));
        }
        return (T) result;
    }

    /**
     * Checks if the given field with the given modifier.
     *
     * @param field    the given field to check.
     * @param modifier the modifier of the given field.
     * @return {@code true} if the field has the given modifier, otherwise {@code false}.
     */
    private static boolean hasModifier(Field field, int modifier) {
        return (field.getModifiers() & modifier) != 0;
    }

    /**
     * Deep-copy the given object with a new instance.
     *
     * @param src     the given object to be deep-copied.
     * @param visited the copied object collection.
     * @param <T>     the type of the given object.
     * @return the deep-copied object instance.
     * @throws NoSuchFieldException
     * @throws IllegalAccessException
     * @throws InstantiationException
     */
    private static <T> T copyObject(T src, Map<Object, Object> visited) throws NoSuchFieldException, IllegalAccessException, InstantiationException {
        Object result = getUnsafe().allocateInstance(src.getClass());
        visited.put(src, result);
        Class<?> clazz = src.getClass();
        do {
            Field[] fields = clazz.getDeclaredFields();
            for (Field field : fields) {
                // if the field is static or final we won't do anything with it
                if (hasModifier(field, Modifier.STATIC) || hasModifier(field, Modifier.FINAL)) {
                    continue;
                }
                field.setAccessible(true);
                // if it's primitive, we just set it's value to our new instance
                // otherwise we recursively copy it's value
                if (Primitives.unwrap(field.getType()).isPrimitive()) {
                    field.set(result, field.get(src));
                } else {
                    field.set(result, doCopy(field.get(src), visited));
                }
            }
            clazz = clazz.getSuperclass();
        } while (clazz != null);
        return (T) result;
    }

    /**
     * Deep-copy the given object that not map type.
     *
     * @param source the original object.
     * @return the deep-copied object instance.
     */
    public static <T> T deepCopyNotMap(T source) {
        try {
            return CopyUtil.copy(source);
        } catch (Exception e) {
            log.error("Failed to deep-copy the given object.", e);
            return null;
        }
    }

    /**
     * Deep-copy the given object.
     *
     * @param source the original object.
     */
    public static void deepCopyMap(Map source, Map target) {
        try {
            for (Object key : source.keySet()) {
                Object originObj = source.get(key);
                Object copyObj = CopyUtil.copy(originObj);
                target.put(key, copyObj);
            }
        } catch (Exception e) {
            log.error("Failed to deep-copy the given map object.", e);
        }
    }
}
    

这个工具代码可以保证复杂结构的对象深拷贝的多线程安全和避免调用对象的构造器,同时根据需要可以决定是否过滤掉static和final修饰的属性和值。感兴趣的朋友可以研究研究。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
Java中的对象拷贝有两种方式:浅拷贝深拷贝。 浅拷贝是指创建一个新对象,新对象中的引用类型字段仍然指向原对象中对应字段的引用。这意味着修改新对象中的引用类型字段也会影响原对象。可以通过实现 Cloneable 接口并重写 clone() 方法来实现浅拷贝。 示例代码如下: ```java class MyClass implements Cloneable { private int value; private MyObject myObject; public MyClass(int value, MyObject myObject) { this.value = value; this.myObject = myObject; } // 重写 clone() 方法 @Override protected Object clone() throws CloneNotSupportedException { return super.clone(); } } class MyObject { // ... } public class Main { public static void main(String[] args) throws CloneNotSupportedException { MyObject obj = new MyObject(); MyClass obj1 = new MyClass(10, obj); // 浅拷贝 MyClass obj2 = (MyClass) obj1.clone(); // obj1 和 obj2 是两个独立的对象,但是它们的 myObject 字段引用同一个对象 System.out.println(obj1 == obj2); // false System.out.println(obj1.myObject == obj2.myObject); // true } } ``` 深拷贝是指创建一个新对象,同时递归地复制原对象及其引用类型字段所引用的对象。这样在修改新对象时不会影响原对象。可以通过实现 Serializable 接口并使用序列化/反序列化来实现深拷贝。 示例代码如下: ```java import java.io.*; class MyClass implements Serializable { private int value; private MyObject myObject; public MyClass(int value, MyObject myObject) { this.value = value; this.myObject = myObject; } // 深拷贝 public MyClass deepClone() throws IOException, ClassNotFoundException { ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(); ObjectOutputStream objectOutputStream = new ObjectOutputStream(byteArrayOutputStream); objectOutputStream.writeObject(this); ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(byteArrayOutputStream.toByteArray()); ObjectInputStream objectInputStream = new ObjectInputStream(byteArrayInputStream); return (MyClass) objectInputStream.readObject(); } } class MyObject implements Serializable { // ... } public class Main { public static void main(String[] args) throws IOException, ClassNotFoundException { MyObject obj = new MyObject(); MyClass obj1 = new MyClass(10, obj); // 深拷贝 MyClass obj2 = obj1.deepClone(); // obj1 和 obj2 是两个独立的对象,它们的 myObject 字段引用不同的对象 System.out.println(obj1 == obj2); // false System.out.println(obj1.myObject == obj2.myObject); // false } } ``` 上述代码中,通过使用序列化/反序列化实现了深拷贝。在 `deepClone()` 方法中,首先将对象写入字节数组输出流,然后通过字节数组输入流读取字节数组并反序列化为一个新的对象。这样就能够得到一个与原对象完全独立的新对象,包括其引用类型字段所引用的对象

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值