Java中的浅拷贝与深拷贝
一、前言
在Java中,拷贝(Copy)是指复制一个对象或数据的值,并创建一个新的对象或数据,而不是简单地引用原始对象。Java中的拷贝操作可以分为两种类型:浅拷贝(Shallow Copy)和深拷贝(Deep Copy)。
二、拷贝操作
- 浅拷贝
浅拷贝创建一个新对象,该对象与原始对象共享部分或全部属性的引用。换句话说,浅拷贝只复制对象的引用,而不复制引用指向的对象本身。这意味着对于原始对象和浅拷贝对象的某些属性的修改会相互影响。在Java中,可以使用clone()
方法来进行浅拷贝。 - 深拷贝
深拷贝创建一个新对象,并复制原始对象的所有属性及其引用指向的对象。换句话说,深拷贝会递归复制对象及其关联的所有对象,从而生成一个完全独立的对象。这意味着对于原始对象和深拷贝对象的任何修改都不会相互影响。在Java中,可以通过实现Serializable
接口或自定义拷贝方法来实现深拷贝。
注意:对于对象中的引用类型属性,拷贝操作可能会涉及到对引用对象的拷贝方式。如果是浅拷贝,那么引用对象将被共享;如果是深拷贝,那么引用对象也将被递归拷贝。
三、clone()实现浅拷贝
- 目标类需要实现
Cloneable
接口,该接口是一个标记接口,表示类支持克隆操作。 - 在目标类中,需要重写
Object
类的clone()
方法,并将访问修饰符设置为public
。注:文中省略了setter和getter方法
public class User implements Cloneable { private String username; private Integer age; // 只有一个String类型的value属性 private Hobby hobby; @Override public Object clone() throws CloneNotSupportedException { return super.clone(); } }
结果记录:public static void main(String[] args) { // 原始对象 User source = new User(); source.setUsername("admin"); source.setAge(18); source.setHobby(new Hobby("游泳")); // clone()实现浅拷贝 try { User target = (User) source.clone(); // 打印各自的值 System.out.println("===修改前==="); System.out.println(source); System.out.println(target); // 修改值后再打印 target.getHobby().setValue("跑步"); System.out.println("===修改后==="); System.out.println(source); System.out.println(target); } catch (CloneNotSupportedException e) { throw new RuntimeException(e); } }
===修改前=== User{username='admin , age=18, hobby=Hobby{value='游泳'}} User{username='admin , age=18, hobby=Hobby{value='游泳'}} ===修改后=== User{username='admin , age=18, hobby=Hobby{value='跑步'}} User{username='admin , age=18, hobby=Hobby{value='跑步'}}
四、序列化实现深拷贝
- 目标类需要实现
Serializable
接口,表示该类支持序列化。public class User implements Serializable { private String username; private Integer age; private Hobby hobby; }
public class Hobby implements Serializable { private String value; }
结果记录:public static void main(String[] args) { // 原始对象 User source = new User(); source.setUsername("admin"); source.setAge(18); source.setHobby(new Hobby("游泳")); // 序列化实现深拷贝 try { // 将原始对象写入字节流 ByteArrayOutputStream baos = new ByteArrayOutputStream(); ObjectOutputStream oos = new ObjectOutputStream(baos); oos.writeObject(source); oos.close(); // 从字节流中读取对象,进行反序列化 ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray()); ObjectInputStream ois = new ObjectInputStream(bais); User target = (User) ois.readObject(); // 打印各自的值 System.out.println("===修改前==="); System.out.println(source); System.out.println(target); // 修改值后再打印 target.getHobby().setValue("跑步"); System.out.println("===修改后==="); System.out.println(source); System.out.println(target); } catch (IOException | ClassNotFoundException e) { throw new RuntimeException(e); } }
===修改前=== User{username='admin , age=18, hobby=Hobby{value='游泳'}} User{username='admin , age=18, hobby=Hobby{value='游泳'}} ===修改后=== User{username='admin , age=18, hobby=Hobby{value='游泳'}} User{username='admin , age=18, hobby=Hobby{value='跑步'}}
五、Spring中的BeanUtils.copyProperties()方法
- 在 Spring 框架中,
BeanUtils.copyProperties()
是一个常用的工具方法,用于将一个 Java 对象的属性值复制到另一个对象中。这个方法可以实现对象之间的属性拷贝,包括基本类型、引用类型和集合类型的属性。 BeanUtils.copyProperties()
方法的语法如下:public static void copyProperties(Object source, Object target)
其中,
source
是源对象,target
是目标对象。该方法会将source
对象的属性值复制到target
对象中,属性名称和类型相同的属性会被复制。如果source
对象的属性在target
对象中不存在,则会被忽略。需要注意的是,BeanUtils.copyProperties()
方法执行的是浅拷贝,即对于引用类型的属性,只复制了引用而不是创建新的对象。如果需要实现深拷贝,可以考虑使用其他方式,比如手动逐个复制属性或者使用其他工具库。此外,BeanUtils.copyProperties()
方法要求源对象和目标对象的属性名称和类型相同,否则可能会出现类型转换错误或属性丢失的问题。- 使用示例:
结果记录:public static void main(String[] args) { // 原始对象 User source = new User(); source.setUsername("admin"); source.setAge(18); source.setHobby(new Hobby("游泳")); // 目标 User target = new User(); BeanUtils.copyProperties(source, target); // 打印各自的值 System.out.println("===修改前==="); System.out.println(source); System.out.println(target); // 修改值后再打印 target.getHobby().setValue("跑步"); System.out.println("===修改后==="); System.out.println(source); System.out.println(target); }
===修改前=== User{username='admin , age=18, hobby=Hobby{value='游泳'}} User{username='admin , age=18, hobby=Hobby{value='游泳'}} ===修改后=== User{username='admin , age=18, hobby=Hobby{value='跑步'}} User{username='admin , age=18, hobby=Hobby{value='跑步'}}