利用序列化实现深度克隆

最近发现项目一个BUG,代码如下:

public static Application getPolicyMap(String productKind, Application applicationUnderWriting) {
		Application application = new Application();
		if (applicationUnderWriting.getPolicyMapKeyResult() != null && applicationUnderWriting.getPolicyMapKeyResult().size() > 0) {
			for (PolicyMapKeyBO policyMapKeyBO : applicationUnderWriting.getPolicyMapKeyResult()) {
				if (productKind.equals(policyMapKeyBO.getTypeName())) {
					application.getPolicyMapKeyResult().add(policyMapKeyBO);
					application.getPolicyMapKeyResult().get(0).setTypeCode(Constants.MAINTYPECODE);
				}
			}
		}
	}

对以上代码做一下简单介绍:applicationUnderWriting传入的是一个全局变量,application.getPolicyMapKeyResult().add(policyMapKeyBO);此处将policyMapKeyBO的引用指向了application的PolicyMapKeyResult,application.getPolicyMapKeyResult().get(0).setTypeCode(Constants.MAINTYPECODE);然后在此处改变了policyMapKeyBO的TypeCode值,自然就改变了全局变量applicationUnderWriting,从而使后续流程无法进行(此处有业务校验)。

针对此问题,我想到了利用序列化实现深度克隆,有一下两种方法可以实现。

方法一:

import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class DeepCloneUtils implements Serializable{

	/**
	 * 
	 */
	private static final long serialVersionUID = 3747630134243407984L;
	private static final Logger logger = LoggerFactory.getLogger(DeepCloneUtils.class);
	
	@SuppressWarnings("unchecked")
	public static <T> T clone(T sourceObj) {
		ByteArrayOutputStream byteArrayOutputStream = null;
		ObjectOutputStream objectOutputStream = null;
		ByteArrayInputStream byteArrayInputStream = null;
		ObjectInputStream objectInputStream = null;
		try {
			// 将sourceObj对象序列化成流,因为写在流里的是对象的一个拷贝,而原对象仍然存在于JVM里面。
			// 所以利用这个特性可以实现对象的深拷贝
			byteArrayOutputStream = new ByteArrayOutputStream();
			objectOutputStream = new ObjectOutputStream(byteArrayOutputStream);
			objectOutputStream.writeObject(sourceObj);
			// 将流序列化成对象
			byteArrayInputStream = new ByteArrayInputStream(byteArrayOutputStream.toByteArray());
			objectInputStream = new ObjectInputStream(byteArrayInputStream);
			Object targetOjb = objectInputStream.readObject();
			return (T) targetOjb;
		} catch (ClassNotFoundException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		} finally {
			// 关闭流
			try {
				byteArrayOutputStream = null;
				byteArrayInputStream = null;
				if (objectOutputStream != null) {
					objectOutputStream.close();
				}
				if (objectInputStream != null) {
					objectInputStream.close();
				}
			} catch (IOException e) {
				logger.error(ExceptionUtil.getErrorMsg(e),e);
			}
		}
		return null;
	}
}

使用此方法的前提是,被序列化的对象sourceObj必须是可序列化的,即实现Serializable标识接口。否则会抛出java.io.NotSerializableException异常。

方法二:

/**
 * 使用protostuff来进行javabean的序列化
 * 
 */
public class ProtostuffUtil {


    /**
     * 序列化
     * 
     * @param obj
     * @return
     */
    public static <T> byte[] serializer(T obj) {
        @SuppressWarnings("unchecked")
        Class<T> clazz = (Class<T>) obj.getClass();
        LinkedBuffer buffer = LinkedBuffer
                .allocate(LinkedBuffer.DEFAULT_BUFFER_SIZE);
        try {
            Schema<T> schema = getSchema(clazz);
            return ProtostuffIOUtil.toByteArray(obj, schema, buffer);
        } catch (Exception e) {
            throw new IllegalStateException(e.getMessage(), e);
        } finally {
            buffer.clear();
        }
    }

    /**
     * 反序列化
     * 
     * @param data
     * @param clazz
     * @return
     */
    public static <T> T deserializer(byte[] data, Class<T> clazz) {
        try {
            T obj = objenesis.newInstance(clazz);
            Schema<T> schema = getSchema(clazz);
            ProtostuffIOUtil.mergeFrom(data, obj, schema);
            return obj;
        } catch (Exception e) {
            throw new IllegalStateException(e.getMessage(), e);
        }
    }

}

此方法,被序列化的对象obj在不实现 Serializable标识接口的情况下同样可以被序列化。次方法使用protostuff夹包。


最后我们改写代码就达到了目的:

public static Application getPolicyMap(String productKind, Application applicationUnderWriting) {
		Application application = new Application();
		ApplicationForService applicationForService = new ApplicationForService();
		byte [] reqRPUnderWritingByte = ProtostuffUtil.serializer(reqRPUnderWriting);
		application = ProtostuffUtil.deserializer(reqRPUnderWritingByte, Application.class);
		if (applicationUnderWriting.getPolicyMapKeyResult() != null && applicationUnderWriting.getPolicyMapKeyResult().size() > 0) {
			for (PolicyMapKeyBO policyMapKeyBO : applicationUnderWriting.getPolicyMapKeyResult()) {
				if (productKind.equals(policyMapKeyBO.getTypeName())) {
					application.getPolicyMapKeyResult().add(policyMapKeyBO);
					application.getPolicyMapKeyResult().get(0).setTypeCode(Constants.MAINTYPECODE);
				}
			}
		}
	}

浅克隆:被复制对象的所有变量都含有与原来的对象相同的值,而所有的对其他对象的引用仍然指向原来的对象。换言之,浅拷贝仅仅复制所考虑的对象,而不复制它所引用的对象。
深克隆:被复制对象的所有变量都含有与原来的对象相同的值,除去那些引用其他对象的变量。那些引用其他对象的变量将指向被复制过的新对象,而不再是原有的那些被引用的对象。




评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值