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;@Overridepublic 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='跑步'}}