剑客
关注科技互联网

每日一博|Java 中对象的深复制和浅复制

1.浅复制与深复制概念

⑴浅复制(浅克隆)

被复制对象的所有变量都含有与原来的对象相同的值,而所有的对其他对象的引用仍然指向原来的对象。换言之,浅复制仅仅复制所考虑的对象,而不复制它所引用的对象。  举例说明:

常见的List的克隆方式有很多,下面我们来列举几种常见的List浅复制的方式:

public static void main(String []args){

List

> list1 = new ArrayList
>();


Map
map = new HashMap ();

map.put("name", "xiaoming");

map.put("age", "28");

list1.add(map);

//克隆方法1:利用原list1作为参数直接构造方法生成。

List

> list2 = new ArrayList
>(list1);


//克隆方法2:手动遍历将原list1中的元素全部添加到复制表中。

for(int i = 0, l = list1.size(); i < l; i++)

list2.add(list1.get(i));   //克隆方法3:调用Collections的静态工具方法 Collections.copy

//克隆方法4:使用System.arraycopy方法进行复制

}

我们知道List自身是一个对象,他在存储类类型的时候,只负责存储地址。而存储基本类型的时候,存储的就是实实在在的值。其实上边的程序也说明了这点,所以纵然你有千千万万个List,元素还是那么几个。无论是重新构造,Collections的复制方法,System的复制方法,还是手动去遍历,结果都一样,这些方法都只改变了ArrayList对象的本身,简单的添加了几个指向老元素的地址。而没做深层次的复制。(压根没有没有 new新对象 的操作出现。)

当然有的时候我们确实需要将这些元素也都复制下来而不是只是用原来的老元素。然而很难在List层实现这个问题。毕竟依照java的语言风格,也很少去直接操作这些埋在堆内存中的数据,所有的操作都去针对能找到他们的地址了。地址没了自身还会被GC干掉。所以只好一点点的去遍历去用new创建新的对象并赋予原来的值。不过据说国外的某位大神可能觉得上述的做法略微鬼畜,所以巧用序列化对象让这些数据在IO流中360度跑了一圈,居然还真的成功复制了。其实把对象序列化到流中,java语言实在是妥协了,毕竟这把你不能再把地址给我扔进去吧?再说了io流是要和别的系统交互的,你发给别人一个地址让别人去哪个堆里找?所以不用多提肯定要新开辟堆内存的。

⑵深复制(深克隆)之序列化

被复制对象的所有变量都含有与原来的对象相同的值,除去那些引用其他对象的变量。那些引用其他对象的变量将指向被复制过的新对象,而不再是原有的那些被引用的对象。换言之,深复制把要复制的对象所引用的对象都复制了一遍。

Java中利用串行化来做深复制(深克隆)(主要是为了避免重写比较复杂对象的深复制的clone()方法,也可以程序实现断点续传等等功能)

把对象写到流里的过程是串行化(Serilization)过程,但是在Java程序师圈子里又非常形象地称为“冷冻”或者“腌咸菜(picking)”过程;而把对象从流中读出来的并行化(Deserialization)过程则叫做 “解冻”或者“回鲜(depicking)”过程。      应当指出的是,写在流里的是对象的一个拷贝,而原对象仍然存在于JVM里面,因此“腌成咸菜”的只是对象的一个拷贝,Java咸菜还可以回鲜。      在Java语言里深复制一个对象,常常可以先使对象实现Serializable接口,然后把对象(实际上只是对象的一个拷贝)写到一个流里(腌成咸菜),再从流里读出来(把咸菜回鲜),便可以重建对象。  如下为深复制源代码。

public List
> deepClone(Object obj) throws IOException,OptionalDataException,ClassNotFoundException{

//将对象写到流里

ByteArrayOutoutStream bo=new ByteArrayOutputStream();

ObjectOutputStream oo=new ObjectOutputStream(bo);

oo.writeObject(obj);//从流里读出来

ByteArrayInputStream bi=new ByteArrayInputStream(bo.toByteArray());

ObjectInputStream oi=new ObjectInputStream(bi);

return(oi.readObject());

}

这样做的前提是对象以及对象内部所有引用到的对象都是可串行化的,否则,就需要仔细考察那些不可串行化的对象或属性可否设成transient,从而将之排除在复制过程之外。

分享到:更多 ()

评论 抢沙发

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址