本以为这个方法可以方便的clone对象,但写了下测试用例,发现不是
by the way 我用的是commons-beanutils-1.8.2.jar 和 Junit3.jar ,最开始少jar
要commons-logging.jar,看下官网
The commons-beanutils package requires that the following additional packages be available in the application's class path at runtime: Logging Package (Apache Commons), version 1.0 or later Furthermore, if any of the following commons-beanutils classes are used: org.apache.commons.beanutils.BeanMap org.apache.commons.beanutils.BeanPredicate org.apache.commons.beanutils.BeanPropertyValueChangeClosure org.apache.commons.beanutils.BeanPropertyValueEqualsPredicate org.apache.commons.beanutils.BeanToPropertyValueTransformer then the following packages must also be available in the application's class path at runtime: Collections Package (Apache Commons), version 1.0 or later
Teacher类
package clone; public class Teacher { private String name; public Teacher(String name) { super(); this.name = name; } public Teacher() { } public String getName() { return name; } public void setName(String name) { this.name = name; } }
Class类
package clone; public class Class { private int num; private Teacher sir; public Class(int num, Teacher sir) { super(); this.num = num; this.sir = sir; } public Class() { } public int getNum() { return num; } public void setNum(int num) { this.num = num; } public Teacher getSir() { return sir; } public void setSir(Teacher sir) { this.sir = sir; } }
测试用例
package clone; import junit.framework.TestCase; import org.apache.commons.beanutils.BeanUtils; public class Test extends TestCase { public void testClone() throws Exception { Teacher sir = new Teacher("sir1"); Class c = new Class(1, sir); Class c2 = (Class) BeanUtils.cloneBean(c); System.out.println(c.getSir() == c2.getSir()); assertNotSame(c, c2); assertSame(c.getSir(), c2.getSir()); c.getSir().setName("xx"); assertEquals(c2.getSir().getName(), "xx"); System.out.println("clone is not deep"); } public void testClone2() throws Exception { Teacher sir = new Teacher("sir1"); Class c = new Class(1, sir); Teacher sir2 = (Teacher) BeanUtils.cloneBean(c.getSir()); Class c2 = new Class(); BeanUtils.copyProperties(c2, c); c2.setSir(sir2); assertNotSame(c, c2); assertNotSame(c.getSir(), c2.getSir()); c.getSir().setName("xx"); assertFalse(c2.getSir().getName().equals("xx")); System.out.println("clone is deep"); } }
用例改成了最后能够pass的,
BeanUtils.cloneBean() 在源码上看是调用了 getPropertyUtils().copyProperties(newBean, bean);
最后实际上还是复制的引用 ,所以第一个方法无法实现深clone
但BeanUtils还是可以帮助我们减少工作量的,假如类的属性不是基础类型的话(即自定义类),可以先clone出那个自定义类,在把他付给新的类,覆盖原来类的引用,具体做法见第二个测试方法,
不过假如对象自定义类属性多,级别深的话,这样还是很麻烦
在teacher class类上面都写了一个默认的clone方法后
@Override protected Object clone() throws CloneNotSupportedException { // TODO Auto-generated method stub return super.clone(); }
我又写了个测试方法 和第2个基本一样
public void testClone3() throws Exception { Teacher sir = new Teacher("sir1"); Class c = new Class(1, sir); Teacher sir2 = (Teacher) c.getSir().clone(); Class c2 = (Class) c.clone(); c2.setSir(sir2); assertNotSame(c, c2); assertNotSame(c.getSir(), c2.getSir()); c.getSir().setName("xx"); assertFalse(c2.getSir().getName().equals("xx")); System.out.println("clone is deep"); }
pass
看来BeanUtils.cloneBean()是为那些本身没有实现clone方法的类准备的
还有一种序列化的方法 这种方法要求所有属性 对象都是实现了序列化接口
package com.test3; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.Serializable; //利用序列化来做深复制 //深clone public class DeepCloneTest { public static void main(String[] args) throws Exception{ //teacher对象将不被clone出来的Student对象共享. Teacher teacher = new Teacher(); teacher.setAge(40); teacher.setName("Teacher zhang"); Student student1 = new Student(); student1.setAge(20); student1.setName("zhangsan"); student1.setTeacher(teacher); //复制出来一个对象student2 Student student2 = (Student)student1.deepCopy(); System.out.println(student2.getAge()); System.out.println(student2.getName()); System.out.println("~~~~~~~~~~~~~~~~~~~~~~"); System.out.println(student1.getTeacher().getAge()); System.out.println(student1.getTeacher().getName()); //修改student2的引用对象 student2.getTeacher().setAge(50); student2.getTeacher().setName("Teacher Li"); System.out.println("~~~~~~~~~~~~~~~~~~~~~~"); System.out.println(student1.getTeacher().getAge()); System.out.println(student1.getTeacher().getName()); } } class Teacher implements Serializable{ private static final long serialVersionUID = -8834559347461591191L; public int age; public String name; public int getAge() { return age; } public void setAge(int age) { this.age = age; } public String getName() { return name; } public void setName(String name) { this.name = name; } } class Student implements Serializable{ //serialVersionUID 如果你的对象序列化后存到硬盘上面后,可是后来你却更改了类的field(增加或减少或改名),当你反序列化时,就会出现Exception的,这样就会造成不兼容性的问题。 //但当serialVersionUID相同时,它就会将不一样的field以type的缺省值赋值(如int型的是0,String型的是null等),这个可以避开不兼容性的问题。所以最好给serialVersionUID赋值 private static final long serialVersionUID = 7991552226614088458L; public int age ; public String name; public Teacher teacher; public int getAge() { return age; } public void setAge(int age) { this.age = age; } public String getName() { return name; } public void setName(String name) { this.name = name; } public Teacher getTeacher() { return teacher; } public void setTeacher(Teacher teacher) { this.teacher = teacher; } public Object deepCopy() throws Exception{ //将该对象序列化成流,因为写在流里的是对象的一个拷贝,而原对象仍然存在于JVM里面。所以利用这个特性可以实现对象的深拷贝 ByteArrayOutputStream bos = new ByteArrayOutputStream(); ObjectOutputStream oos = new ObjectOutputStream(bos); oos.writeObject(this); //将流序列化成对象 ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray()); ObjectInputStream ois = new ObjectInputStream(bis); return ois.readObject(); } } 输出结果为: 20 zhangsan ~~~~~~~~~~~~~~~~~~~~~~ 40 Teacher zhang ~~~~~~~~~~~~~~~~~~~~~~ 40 Teacher zhang