当前位置:

如何实现对象拷贝

机器助手 |  2025-07-13
 100人浏览

1.答案(第三种可不答)

  • 实现Cloneable接口并重写Object类中的clone()方法
  • 实现Serializable接口,通过对象的序列化和反序列化实现克隆,可以实现真正的深度克隆
  • 通过org.apache.commons中的工具类BeanUtils和PropertyUtils进行对象复制

2.浅克隆和深克隆的区别

  • 现有Employee类,其中有成员属性为Person类对象

 

public class Employee implements Cloneable{ private String name; private int age; private Person person; }

  • 当我们进行浅克隆的时候,例如将e1 克隆给 e2 ,这时候输出e1,e2发现是实现了克隆,但是实际上e2和e1中的person对象却还是指向了同一个地址
  • 也就是说但我们修改了e1中的person属性,那么e2中的person属性也会被修改
  • 而深克隆实现后e1,和e2就是完全独立的两个对象,不会相互干扰

3.实现Cloneable接口实现深克隆和浅克隆

  • 首先定义Person类

 

public class Person { private String name; private int age; private int sex; //下面省略构造器,get set toString方法 }

  • 定义Employee类,实现Cloneable接口,重写clone方法
  • 这里要注意Employee类中有一个成员属性是Person对象,也就是引用对象

 

public class Employee implements Cloneable{ private String name; private int age; private Person person; public Employee() { } public Employee(String name, int age, Person person) { this.name = name; this.age = age; this.person = person; } @Override public Object clone() throws CloneNotSupportedException { return super.clone(); } //下面省略get set toString方法 }

3.1 浅克隆实现

  • 首先我们创建employee 实例对象,然后进行克隆复制给employee1
  • 分别对employee ,employee1 进行输出发现无任何问题
  • 但是当我们对employee中的person成员属性进行修改之后,再分别对employee ,employee1进行输出,发现两者都中的值都修改了!这就是浅克隆
  • 浅克隆:在进行克隆时,克隆后的两个对象中的引用属性都是指向同一个地址,也就是employee ,employee1中的person是指向同一个对象地址,一个修改了,另一个也会修改

 

Person person = new Person("name",10,1); Employee employee = new Employee("fanglei",20,person); System.out.println("employee: "+employee); try { //浅克隆 Employee employee1 = (Employee) employee.clone(); System.out.println("employee1: "+ employee1); //修改employee中person的属性,employee1中的属性也修改了 employee.getPerson().setAge(100); System.out.println("employee: "+employee); System.out.println("employee1: "+ employee1); } catch (CloneNotSupportedException e) { e.printStackTrace(); }

  • 输出结果:可以看到两个都修改为了100

ce04810633854c8fb111b0afff66cc0f

3.2 深克隆实现

  • 通过实现Clonable接口实现深克隆,那么就要该类中的引用对象属性也要实现Clonable接口并且重写clone方法
  • 也就是说Person类也要实现Clonable接口并且重写clone方法
  • 并在Employee类的clone方法中调用Person的clone方法进行克隆复制

下图为修改之后的Person类:其实就是实现Clonable接口并且重写clone方法


 

public class Person implements Cloneable{ private String name; private int age; private int sex; @Override protected Object clone() throws CloneNotSupportedException { return super.clone(); } }

下图为Employee类clone方法修改之后:


 

@Override public Object clone() throws CloneNotSupportedException { Employee employee = null; employee = (Employee) super.clone(); employee.person = (Person) this.person.clone(); return employee; }

再调用刚刚的测试克隆代码:


 

Person person = new Person("name",10,1); Employee employee = new Employee("fanglei",20,person); System.out.println("employee: "+employee); try { //浅克隆 Employee employee1 = (Employee) employee.clone(); System.out.println("employee1: "+ employee1); //修改employee中person的属性,employee1中的属性也修改了 employee.getPerson().setAge(100); System.out.println("employee: "+employee); System.out.println("employee1: "+ employee1); } catch (CloneNotSupportedException e) { e.printStackTrace(); }

输出结果如下:可以看到一个为10,一个为100

cb2249ab50e24eaaad51bde504c0f428

4.实现Serializable接口,实现深克隆

  • Employee修改只需要实现Serializable接口

 

public class Employee implements Serializable{}

  • Person类修改也只需要实现Serializable接口

 

public class Person implements Serializable{}

  • 新增克隆工具类

 

public class CloneUtil { private CloneUtil() { throw new AssertionError(); } /** * Clone. * 调用ByteArrayInputStream或ByteArrayOutputStream对象的close方法没有任何意义, * 这两个基于内存的流只要垃圾回收器清理对象时就能够释放资源,这一点不同于对外部资源(如文件流)的释放。 * * @param obj The object. * @param <T> The type. * @return The cloned object. * @throws Exception The exception. */ public static <T> T clone(T obj) throws Exception { ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(); ObjectOutputStream objectOutputStream = new ObjectOutputStream(byteArrayOutputStream); objectOutputStream.writeObject(obj); ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(byteArrayOutputStream.toByteArray()); ObjectInputStream objectInputStream = new ObjectInputStream(byteArrayInputStream); return (T) objectInputStream.readObject(); } }

  • 调用测试:

 

Person p1 = new Person("name",10,1); Employee e1 = new Employee("fanglei",20,p1); System.out.println("e1: "+e1); try { Employee e2 = CloneUtil.clone(e1); System.out.println("e2: "+ e2); e1.getPerson().setAge(100); System.out.println("e1: "+e1); System.out.println("e2: "+ e2); } catch (CloneNotSupportedException e) { e.printStackTrace(); }

  • 输出结果:发现的确实现了深克隆

3e76663927bf44adb50d2474bd4e0571

5.org.apache.commons中BeanUtils和PropertyUtils工具类实现深克隆

  • BeanUtils提供类型转换功能,即发现两个JavaBean的同名属性为不同类型时,在支持的数据类型范围内进行转换,而PropertyUtils不支持这个功能,但是速度会更快一些。在实际开发中,BeanUtils使用更普遍一点,犯错的风险更低一点。

 

try { Student stu1 = new Student(); stu1.setNumber(12345); Student stu2 = new Student(); BeanUtils.copyProperties(stu2, stu1); } catch (IllegalAccessException e) { e.printStackTrace(); } catch (InvocationTargetException e) { e.printStackTrace(); }


 

try { Student stu1 = new Student(); stu1.setNumber(12345); Student stu2 = new Student(); PropertyUtils.copyProperties(stu2, stu1); catch (NoSuchMethodException e) { e.printStackTrace(); }

文章评论