java深拷贝与c#深拷贝的实现
Posted 酒香逢
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了java深拷贝与c#深拷贝的实现相关的知识,希望对你有一定的参考价值。
楼主是一名asp.net攻城狮,最近经常跑java组客串帮忙开发,所以最近对java的一些基础知识特别上心。却遇到需要将一个对象深拷贝出来做其他事情,而原对象保持原有状态的情况。(实在是不想自己new一个出来,然后对着一堆字段赋值......好吧,再此之前我没有关心是否项目框架有深拷贝的方法),然后就想着用反射实现吧....接下来
是我自己的原因,还是真的不存在这样的纯用反射实现的深拷贝方式....(c#是有纯反射实现的)
但也不能算自己白忙活吧,也找到了其他实现深拷贝的方式(但是每种方式我都觉得并不是太合理,也许是因为c#的方式带入了吧,最后贴出c#版本纯反射实现深拷贝的代码)
先说java的深拷贝方式0.0
实体类:一个轮胎类,一个车辆类,车辆中包含轮胎
1 /**轮胎类**/ 2 public class Tire implements Cloneable { 3 public String color; 4 public int radius; 5 public Tire(){} 6 public Tire(String color, int radius) { 7 this.color = color; 8 this.radius = radius; 9 } 10 11 @Override 12 protected Object clone() throws CloneNotSupportedException { 13 return super.clone(); 14 } 15 } 16 /**车辆类**/ 17 public class Car implements Cloneable{ 18 public String name; 19 public String color; 20 public Tire tire; 21 public Car() {} 22 public Car(String name, String color, Tire tire) { 23 this.name = name; 24 this.color = color; 25 this.tire = tire; 26 } 27 public void whistle(){ 28 System.out.println("汽车"+this.name+" 鸣笛..."); 29 } 30 public String getName() { 31 return name; 32 } 33 public void setName(String name) { 34 this.name = name; 35 } 36 public String getColor() { 37 return color; 38 } 39 public void setColor(String color) { 40 this.color = color; 41 } 42 public Tire getTire() { 43 return tire; 44 } 45 public void setTire(Tire tire) { 46 this.tire = tire; 47 } 48 @Override 49 protected Object clone() throws CloneNotSupportedException { 50 return super.clone(); 51 } 52 }
单元测试:
1 @Test 2 public void test() throws CloneNotSupportedException { 3 Tire tire = new Tire("black",100); 4 Car car = new Car("奔驰","white",tire); 5 Car car_copy = (Car)car.clone(); 6 System.out.println("car:"+car.hashCode()+" car.tire:"+car.tire.hashCode()); 7 System.out.println("car_copy:"+car_copy.hashCode()+" car_copy.tire:"+car_copy.tire.hashCode()); 8 car_copy.color = "blue"; 9 System.out.println("car_copy:"+car_copy.color+" car:"+car.color); 10 }
输出结果:
car:1223737555 car.tire:906199566 car_copy:542081238 car_copy.tire:906199566 car_copy:blue car:white
从结果可以的之,car与car_copy的内存地址并不一致,但car.tire与car_copy.tire的内存地址却是一致的,说明“奔驰”车确实又造出了一辆,但却公用同一幅轮胎(这种情形....哈哈哈),好吧,也就是只复制了tire的引用,这可以说是深拷贝的不彻底 (hashCode()的值可以当作是内存地址来理解),那么要怎样才能彻底,真正的深拷贝?
修改Car类中的clone方法:
1 @Override 2 protected Object clone() throws CloneNotSupportedException { 3 Car car = (Car)super.clone(); 4 car.tire = (Tire)car.tire.clone(); 5 return car; 6 }
输出结果:
car:1223737555 car.tire:906199566 car_copy:542081238 car_copy.tire:1133736492 car_copy:blue car:white
这样最终实现了,但这种方式用到项目中并不是很合适吧,每个需要深拷贝的类,都要实现Cloneable接口,并覆盖其clone方法,遇到引用其他类时候更是需要修改clone方法,要是引用其他类,其他类再引用其他类呢?这不好吧......
实体类:与第一种方式类似,换成实现Serializable接口,去掉clone方法
/**轮胎类**/ @SuppressWarnings("serial") public class Tire implements java.io.Serializable { public String color; public int radius; public Tire(){} public Tire(String color, int radius) { this.color = color; this.radius = radius; } } /**车辆类**/ @SuppressWarnings("serial") public class Car implements java.io.Serializable{ public String name; public String color; public Tire tire; public Car() {} public Car(String name, String color, Tire tire) { this.name = name; this.color = color; this.tire = tire; } public void whistle(){ System.out.println("汽车"+this.name+" 鸣笛..."); } public String getName() { return name; } public void setName(String name) { this.name = name; } public String getColor() { return color; } public void setColor(String color) { this.color = color; } public Tire getTire() { return tire; } public void setTire(Tire tire) { this.tire = tire; } }
深拷贝方法:
1 @SuppressWarnings("unchecked") 2 public static Object deepClone(Object obj) 3 { 4 Object copyObj = null; 5 ObjectOutputStream out = null; 6 ObjectInputStream in = null; 7 try { 8 // 序列化 9 ByteArrayOutputStream bufferOut = new ByteArrayOutputStream(); 10 out = new ObjectOutputStream(bufferOut); 11 12 out.writeObject(obj); 13 14 // 反序列化 15 ByteArrayInputStream bufferIn = new ByteArrayInputStream(bufferOut.toByteArray()); 16 in = new ObjectInputStream(bufferIn); 17 copyObj = in.readObject(); 18 } catch (Exception e) { 19 e.printStackTrace(); 20 throw new RuntimeException(e); 21 }finally{ 22 try{ 23 if(in != null){ 24 in.close(); 25 } 26 if(out!=null){ 27 out.close(); 28 } 29 }catch(IOException e){ 30 throw new RuntimeException(e); 31 } 32 } 33 return copyObj; 34 }
单元测试:
1 @Test 2 public void test() throws CloneNotSupportedException { 3 Tire tire = new Tire("black",100); 4 Car car = new Car("奔驰","white",tire); 5 Car car_copy = (Car)deepClone(car); 6 System.out.println("car:"+car.hashCode()+" car.tire:"+car.tire.hashCode()); 7 System.out.println("car_copy:"+car_copy.hashCode()+" car_copy.tire:"+car_copy.tire.hashCode()); 8 car_copy.color = "blue"; 9 System.out.println("car_copy:"+car_copy.color+" car:"+car.color); 10 }
输出结果:
car:2019524978 car.tire:855703640 car_copy:1407965019 car_copy.tire:545768040 car_copy:blue car:white
从结果集中可以看出是深拷贝是正确的,但是每个类还是需要实现Serializable,好像也不合适吧......
优化一下深拷贝方法:将其换成泛型,这样拷贝出来就不需要强转了(好吧,其实也没比上面的方法好到哪去...)
1 @SuppressWarnings("unchecked") 2 public static <T> T deepClone(T obj) 3 { 4 T copyObj = null; 5 ObjectOutputStream out = null; 6 ObjectInputStream in = null; 7 try { 8 // 序列化 9 ByteArrayOutputStream bufferOut = new ByteArrayOutputStream(); 10 out = new ObjectOutputStream(bufferOut); 11 12 out.writeObject(obj); 13 14 // 反序列化 15 ByteArrayInputStream bufferIn = new ByteArrayInputStream(bufferOut.toByteArray()); 16 in = new ObjectInputStream(bufferIn); 17 copyObj = (T)in.readObject(); 18 } catch (Exception e) { 19 e.printStackTrace(); 20 throw new RuntimeException(e); 21 }finally{ 22 try{ 23 if(in != null){ 24 in.close(); 25 } 26 if(out!=null){ 27 out.close(); 28 } 29 }catch(IOException e){ 30 throw new RuntimeException(e); 31 } 32 } 33 return copyObj; 34 }
通过序列化与反序列化深拷贝还有更简单的实现方式,就是需要导个包(拷贝的类也必须实现Serializable接口),当然,我已经为你们准备好了 点击->org.apache.commons.lang
深拷贝方法:就一行代码...
1 public Object deepClone(Object obj){ 2 return org.apache.commons.lang.SerializationUtils.clone((Serializable)obj); 3 }
好了,java的暂时就到这里了,当然对于这两种方式并不是很满意...
-------------------------------------------------
下面方法是c#的深拷贝,纯反射实现,无需实现任何接口,哦对,需要实体类有个无参的构造方法,简单使用强大,微软大法好啊......有需要用到的同学就拿去用吧,目前经过一个几百W的项目框架中考验,真的强大实用
1 /// <summary> 2 /// 对象拷贝 3 /// </summary> 4 /// <param name="obj">被复制对象</param> 5 /// <returns>新对象</returns> 6 private object CopyOjbect(object obj) { 7 if (obj == null) { 8 return null; 9 } 10 Object targetDeepCopyObj; 11 Type targetType = obj.GetType(); 12 //值类型 13 if (targetType.IsValueType == true) { 14 targetDeepCopyObj = obj; 15 } 16 //引用类型 17 else { 18 targetDeepCopyObj = System.Activator.CreateInstance(targetType); //创建引用对象 19 System.Reflection.MemberInfo[] memberCollection = obj.GetType().GetMembers(); 20 21 foreach (System.Reflection.MemberInfo member in memberCollection) { 22 //拷贝字段 23 if (member.MemberType == System.Reflection.MemberTypes.Field) 24 { 25 System.Reflection.FieldInfo field = (System.Reflection.FieldInfo)member; 26 Object fieldValue = field.GetValue(obj); 27 if (fieldValue is ICloneable) 28 { 29 field.SetValue(targetDeepCopyObj, (fieldValue as ICloneable).Clone()); 30 } 31 else 32 { 33 field.SetValue(targetDeepCopyObj, CopyOjbect(fieldValue)); 34 } 35 36 }//拷贝属性 37 else if (member.MemberType == System.Reflection.MemberTypes.Property) { 38 System.Reflection.PropertyInfo myProperty = (System.Reflection.PropertyInfo)member; 39 40 MethodInfo info = myProperty.GetSetMethod(false); 41 if (info != null) { 42 try { 43 object propertyValue = myProperty.GetValue(obj, null); 44 if (propertyValue is ICloneable) { 45 myProperty.SetValue(targetDeepCopyObj, (propertyValue as ICloneable).Clone(), null); 46 } 47 else { 48 myProperty.SetValue(targetDeepCopyObj, CopyOjbect(propertyValue), null); 49 } 50 } 51 catch (System.Exception ex) { 52 53 } 54 } 55 } 56 } 57 } 58 return targetDeepCopyObj; 59 }
以上是关于java深拷贝与c#深拷贝的实现的主要内容,如果未能解决你的问题,请参考以下文章