一、初探对象拷贝:浅尝辄止与深入骨髓
class Address {String city;}class Person {String name;Address address;}// 浅拷贝示例Person original = new Person();original.name = "Alex";original.address = new Address();original.address.city = "Beijing";Person shallowCopy = new Person();shallowCopy.name = original.name;shallowCopy.address = original.address; // 浅拷贝:地址引用传递original.address.city = "Shanghai"; // 改变原对象地址System.out.println(shallowCopy.address.city); // 输出:Shanghai
2. 深拷贝:灵魂的克隆
与浅拷贝不同,深拷贝不仅复制对象本身,还包括其内部所有引用对象的独立复制,确保每个对象在内存中的独立性,任何一方的改变都不会干扰到另一方。
// 深拷贝实现:通过Cloneable接口class Person implements Cloneable {String name;Address address;protected Person clone() throws CloneNotSupportedException {Person cloned = (Person) super.clone();cloned.address = new Address(); // 创建地址的新实例cloned.address.city = this.address.city; // 复制地址内容return cloned;}}// 使用深拷贝Person deepCopy = original.clone(); // 独立的地址对象
二、项目实战:拷贝的几种方式
ObjectOutputStream与ObjectInputStream),通过写入和读取对象的字节流实现深拷贝。这种方法通用性强,但性能开销较大,特别是对于大对象或频繁操作。// 序列化实现深拷贝public static <T> T deepClone(T obj) throws IOException, ClassNotFoundException {ByteArrayOutputStream baos = new ByteArrayOutputStream();ObjectOutputStream oos = new ObjectOutputStream(baos);oos.writeObject(obj);ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());ObjectInputStream ois = new ObjectInputStream(bais);return (T) ois.readObject();}
2. JSON库转换
// 使用Gson实现深拷贝Gson gson = new Gson();String json = gson.toJson(original);Person fromJson = gson.fromJson(json, Person.class);
BeanUtils.copyProperties()方法能够方便地实现对象的浅拷贝。import org.apache.commons.beanutils.BeanUtils;Person original = ...;Person copy = new Person();BeanUtils.copyProperties(copy, original);
注意:默认情况下进行浅拷贝,但可以通过自定义转换器实现深层次拷贝,该方法,性能较差,不建议使用,阿里巴巴Java开发规约也不建议使用。
4. Spring的BeanUtils.copyProperties()
import org.springframework.beans.BeanUtils;// 假设Person和AnotherPerson有相同的属性结构Person source = new Person("Alice", new Address("New York"));AnotherPerson target = new AnotherPerson();// 浅拷贝BeanUtils.copyProperties(source, target);
注意:在这个过程中,Address对象依然是共享的,任何对target的Address属性的修改会影响到source的Address。另外该方法参数和上面Apache Commons BeanUtils参数是相反的,Spring的是源。
三、注意事项:拷贝路上的坑与避雷针
- 性能考量:深拷贝通常比浅拷贝消耗更多资源,尤其是递归拷贝深层对象时,需根据实际需求权衡。
- 类设计:确保所有参与拷贝的对象支持深拷贝,特别是当使用序列化或反射技术时。
- 异常处理:实现Cloneable接口时,需妥善处理
CloneNotSupportedException。 - 内存泄漏:深拷贝大量数据时要警惕内存占用,及时释放不再使用的对象引用。
- 第三方库选择:使用JSON库或其他序列化工具时,关注其版本兼容性与潜在的安全风险。
四、结语
掌握Java对象的深浅拷贝,不仅仅是技术层面的修炼,更是对软件工程思想的深刻理解。希望本文的分享,能助你在开发的道路上更进一步,成就更加优雅高效的程序设计!
原创文章,作者:guozi,如若转载,请注明出处:https://www.sudun.com/ask/87843.html