本文共 3461 字,大约阅读时间需要 11 分钟。
克隆模式是一种比较简单的设计模式,基本从字面意思就可以明白这种设计模式是干什么的,简单来说就是造一个和原来一模一样的对象,就叫克隆模式。克隆模式分为两种,一种是浅度克隆,一种是深度克隆。
在《Java核心技术卷I》中,有一条编码规范——成员变量是对象,使用get()方法的时候不建议直接return这个对象,而是clone一份出来。
为什么要这么做呢?
问题描述:
//数据对象public class Data { private String[] keys; public String[] getKeys() { return keys; } public void setKeys(String[] keys) { this.keys = keys; }}//政策对象,包含数据对象(成员变量)public class Policy { private String policyId; private String desc; private Data data; public String getPolicyId() { return policyId; } public void setPolicyId(String policyId) { this.policyId = policyId; } public String getDesc() { return desc; } public void setDesc(String desc) { this.desc = desc; } public Data getData() { return data; } public void setData(Data data) { this.data = data; }}
如果我们获取到了Policy对象,并使用get方法获取了Data对象,一旦因为一些特殊的业务逻辑,而对Data对象进行了set设置操作,比如修改了某个key,那么原Policy中的Data对象的值也变了(被覆盖),这是很致命的。根本原因就在于,在java中他们指向同一份内存地址。
让Policy对象遵从Cloneable接口,并重写Object的clone方法,注意:重新的克隆方法要将默认的protected限定词改为public
//遵从Cloneable接口public class Policy implements Cloneable { /*省略代码。。。*/ /** * 克隆方法-注意必须改成public * @return * @throws CloneNotSupportedException */ @Override public Object clone() throws CloneNotSupportedException { return super.clone(); }}
我们测试一下,打印地址
public static void main(String[] args) throws CloneNotSupportedException { String[] keys = { "1", "2"}; Data data = new Data(); data.setKeys(keys); Policy policy = new Policy(); policy.setPolicyId(UUID.randomUUID().toString()); policy.setDesc("普通政策"); policy.setData(data); Policy policyClone = (Policy)policy.clone(); System.out.println("policy = " + policy + ", data = " + policy.getData() + ", keys = " + policy.getData().getKeys()); System.out.println("policyClone = " + policyClone + ", data = " + policyClone.getData() + ", keys = " + policyClone.getData().getKeys());}//printpolicy = com.flight.carryprice.bcfuseModel.policy.Policy@6e1ec318, data = com.flight.carryprice.bcfuseModel.policy.Data@7e0b0338, keys = [Ljava.lang.String;@617faa95policyClone = com.flight.carryprice.bcfuseModel.policy.Policy@1e127982, data = com.flight.carryprice.bcfuseModel.policy.Data@7e0b0338, keys = [Ljava.lang.String;@617faa95
我们就会发现,Policy对象地址不一样了,说明我们成功克隆了该对象,对其的基本类型及String类型值的修改,都不会影响原始Policy。
但是,我们还发现一个问题,就是对象中引用的对象Data,其地址并没有发生变化,也就是说,Policy虽然被复制了,但是其中的Data对象没有被引用,克隆程度不够深。一种比较笨的办法就是,层层遵从Cloneable接口,层层重写clone方法,如果嵌套深度很深,这样做无疑是很麻烦的,所以我们可采用序列化方式。
把对象写到流里的过程是序列化过程(Serialization),而把对象从流中读出来的过程则叫做反序列化过程(Deserialization)。
应当指出的是,写在流里的是对象的一个拷贝,而原对象仍然存在于JVM里面。
在Java语言里深复制一个对象,常常可以先使对象实现Serializable接口,然后把对象(实际上只是对象的一个拷贝)写到一个流里,再从流里读出来,便可以重建对象。
这样做的前提是对象以及对象内部所有引用到的对象都是可串行化的,否则,就需要仔细考察那些不可串行化的对象可否设成transient,从而将其排除在复制过程之外。
/*这种方法,内层对象Data与目标对象Policy都必须遵从序列化接口新增Policy的Clone方法*/public Object deepClone() { /*序列化处理write:将对象的拷贝从内存写到流中(内存->流)*/ ByteArrayOutputStream baos = new ByteArrayOutputStream();//内存流 ObjectOutputStream oos = new ObjectOutputStream(baos);//对象流 oos.writeObject(this); /*反序列化处理read:将流中的对象反序列化到内存中(流->内存)*/ ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray()); ObjectInputStream ois = new ObjectInputStream(bais); return ois.readObject();}
转载地址:http://btgzi.baihongyu.com/