你的位置:首页 > Java教程

[Java教程]java深拷贝与c#深拷贝的实现


楼主是一名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   @Override12   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   @Override49   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:906199566car_copy:542081238 car_copy.tire:906199566car_copy:blue car:white

 

从结果可以的之,car与car_copy的内存地址并不一致,但car.tire与car_copy.tire的内存地址却是一致的,说明“奔驰”车确实又造出了一辆,但却公用同一幅轮胎(这种情形....哈哈哈),好吧,也就是只复制了tire的引用,这可以说是深拷贝的不彻底 (hashCode()的值可以当作是内存地址来理解),那么要怎样才能彻底,真正的深拷贝?

修改Car类中的clone方法:

1 @Override2   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:906199566car_copy:542081238 car_copy.tire:1133736492car_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:855703640car_copy:1407965019 car_copy.tire:545768040car_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             else32             {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     }