一、初探对象拷贝:浅尝辄止与深入骨髓
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