【面试精讲】深克隆和浅克隆的实现方式?深克隆和浅克隆有什么区别?
目录
本文导读
一、浅克隆(Shallow Clone)
二、深克隆(Deep Clone)
1、递归使用clone()方法实现深克隆
2、使用序列化实现深克隆
3、通过第三方工具实现深克隆
三、clone() 源码分析
四、Arrays.copyOf()是浅克隆
总结
博主v:XiaoMing_Java
本文导读
在Java编程中,对象的克隆是创建对象副本的过程。这在需要独立修改原始对象和副本对象时非常有用。克隆分为两种类型:浅克隆(Shallow Clone)和深克隆(Deep Clone)。理解这两者的区别对于避免程序中的隐藏bug和内存泄漏至关重要。
一、浅克隆(Shallow Clone)
浅克隆创建一个新对象,其字段值与原始对象相同。对于基本数据类型的字段,浅克隆会直接复制值。但对于引用数据类型,浅克隆并不创建被引用对象的副本,而是复制引用地址。因此,如果原始对象或克隆对象中的一个修改了引用类型的字段,这一变化也会反映在另一个对象上。
实现方式:实现Cloneable
接口并覆盖clone()
方法。
// Person类包含了一个引用类型的字段Address。
public class Person implements Cloneable {
private String name;
private int age;
private Address address; // 引用类型
public Person(String name, int age, Address address) {
this.name = name;
this.age = age;
this.address = address;
}
@Override
protected Object clone() throws CloneNotSupportedException {
// 使用默认的clone()方法进行浅克隆时,Address对象不会被克隆,仅仅复制其引用。
return super.clone();
}
}
二、深克隆(Deep Clone)
深克隆不只是复制对象的直接字段,还包括对象内部所有层次的字段。对于引用数据类型,深克隆会递归克隆所有子对象,从而确保克隆对象与原始对象完全独立,修改一个对象的状态不会影响到另一个。
实现方式:
1、递归使用clone()
方法实现深克隆
public class Address implements Cloneable {
private String city;
public Address(String city) {
this.city = city;
}
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
}
public class Person implements Cloneable {
private String name;
private int age;
private Address address;
@Override
protected Object clone() throws CloneNotSupportedException {
Person cloned = (Person) super.clone();
cloned.address = (Address) address.clone(); // 深克隆
return cloned;
}
}
2、使用序列化实现深克隆
这种方法不需要对每个对象单独实现Cloneable
接口,但要求所有对象都必须是可序列化的(即实现Serializable
接口)。
import java.io.*;
public class DeepCopyUtil {
@SuppressWarnings("unchecked")
public static <T> T deepCopy(T object) {
try {
ByteArrayOutputStream bos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(bos);
oos.writeObject(object);
oos.flush();
ByteArrayInputStream bin = new ByteArrayInputStream(bos.toByteArray());
ObjectInputStream ois = new ObjectInputStream(bin);
return (T) ois.readObject();
} catch (IOException | ClassNotFoundException e) {
throw new RuntimeException(e);
}
}
}
3、通过第三方工具实现深克隆
使用 Apache Commons Lang 来实现深克隆
import org.apache.commons.lang3.SerializationUtils;
import java.io.Serializable;
/**
* 深克隆实现方式四:通过 apache.commons.lang 实现
*/
public class FourthExample {
public static void main(String[] args) throws CloneNotSupportedException {
// 创建对象
Address address = new Address(110, "北京");
People p1 = new People(1, "Java", address);
// 调用 apache.commons.lang 克隆对象
People p2 = (People) SerializationUtils.clone(p1);
// 修改原型对象
p1.getAddress().setCity("西安");
// 输出 p1 和 p2 地址信息
System.out.println("p1:" + p1.getAddress().getCity() + " p2:" + p2.getAddress().getCity());
}
static class People implements Serializable {
private Integer id;
private String name;
private Address address;
}
static class Address implements Serializable {
private Integer id;
private String city;
}
}
三、clone() 源码分析
// clone() 是使用 native 修饰的本地方法,因此执行的性能会很高,并且它返回的类型为 Object,因此在调用克隆之后要把对象强转为目标类型才行。
protected native Object clone() throws CloneNotSupportedException;
对于所有对象来说,x.clone() !=x ,应当返回 true,因为克隆对象与原对象不是同一个对象;
对于所有对象来说,x.clone().getClass() == x.getClass() ,应当返回 true,因为克隆对象与原对象的类型是一样的;
对于所有对象来说,x.clone().equals(x) ,应当返回 true,因为使用 equals 比较时,它们的值都是相同的。
四、Arrays.copyOf()是浅克隆
在修改克隆对象的第一个元素之后,原型对象的第一个元素也跟着被修改了,这说明 Arrays.copyOf() 其实是一个浅克隆。
People[] o1 = {new People(1, "Java")};
People[] o2 = Arrays.copyOf(o1, o1.length);
// 修改原型对象的第一个元素的值
o1[0].setName("Jdk");
System.out.println("o1:" + o1[0].getName());
System.out.println("o2:" + o2[0].getName());
总结
浅克隆与深克隆解决的是Java对象复制的问题,它们之间的主要区别在于是否对对象内部的引用类型进行递归复制。选择哪种克隆方式取决于你的具体需求。对于需要完全独立的对象复制,深克隆是更好的选择。然而,深克隆的实现比浅克隆更复杂,且性能开销较大。正确理解和使用这两种克隆方法,对编写健壮的Java应用程序至关重要。
如果本文对你有帮助 欢迎 关注 、点赞 、收藏 、评论, 博主才有动力持续记录遇到的问题!!!
博主v:XiaoMing_Java
📫作者简介:嗨,大家好,我是 小明(小明Java问道之路),互联网大厂后端研发专家,2022博客之星TOP3 / 博客专家 / CSDN后端内容合伙人、InfoQ(极客时间)签约作者、阿里云签约博主、全网 6 万粉丝博主。
🍅 文末获取联系 🍅 👇🏻 精彩专栏推荐订阅收藏 👇🏻
专栏系列(点击解锁)
学习路线(点击解锁)
知识定位
🔥Redis从入门到精通与实战🔥
Redis从入门到精通与实战
围绕原理源码讲解Redis面试知识点与实战
🔥MySQL从入门到精通🔥
MySQL从入门到精通
全面讲解MySQL知识与企业级MySQL实战 🔥计算机底层原理🔥
深入理解计算机系统CSAPP
以深入理解计算机系统为基石,构件计算机体系和计算机思维
Linux内核源码解析
围绕Linux内核讲解计算机底层原理与并发
🔥数据结构与企业题库精讲🔥
数据结构与企业题库精讲
结合工作经验深入浅出,适合各层次,笔试面试算法题精讲
🔥互联网架构分析与实战🔥
企业系统架构分析实践与落地
行业最前沿视角,专注于技术架构升级路线、架构实践
互联网企业防资损实践
互联网金融公司的防资损方法论、代码与实践
🔥Java全栈白宝书🔥
精通Java8与函数式编程
本专栏以实战为基础,逐步深入Java8以及未来的编程模式
深入理解JVM
详细介绍内存区域、字节码、方法底层,类加载和GC等知识
深入理解高并发编程
深入Liunx内核、汇编、C++全方位理解并发编程
Spring源码分析
Spring核心七IOC/AOP等源码分析
MyBatis源码分析
MyBatis核心源码分析
Java核心技术
只讲Java核心技术