樓主是一名asp.net攻城獅,最近經常跑java組客串幫忙開發,所以最近對java的一些基礎知識特別上心。卻遇到需要將一個對象深拷貝出來做其他事情,而原對象保持原有狀態的情況。(實在是不想自己new一個出來,然後對著一堆字段賦值......好吧,再此之前我沒有關心是否項目框架有深拷貝的方法),然後就想著用反射實現吧....接下來
是我自己的原因,還是真的不存在這樣的純用反射實現的深拷貝方式....(c#是有純反射實現的)
但也不能算自己白忙活吧,也找到了其他實現深拷貝的方式(但是每種方式我都覺得並不是太合理,也許是因為c#的方式帶入了吧,最後貼出c#版本純反射實現深拷貝的代碼)
方式一:實現Cloneable接口,重寫clone方法實體類:一個輪胎類,一個車輛類,車輛中包含輪胎
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接口)實體類:與第一種方式類似,換成實現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#深拷貝 反射實現下面方法是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 }