讲讲深拷贝和浅拷贝?

[复制链接]
发表于 2025-5-16 08:58:43 | 显示全部楼层 |阅读模式

马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。

您需要 登录 才可以下载或查看,没有账号?立即注册

×
关于深拷贝和浅拷贝区别,先给结论:

  • 浅拷贝:浅拷贝会在堆上创建一个新的对象(区别于引用拷贝的一点),不过,假如原对象内部的属性是引用范例的话,浅拷贝会直接复制内部对象的引用所在,也就是说拷贝对象和原对象共用同一个内部对象。
  • 深拷贝:深拷贝会完全复制整个对象,包罗这个对象所包罗的内部对象。
clone()

clone方法是Java中拷贝对象的方法
在Java中拷贝对象其实是指复制一个与原对象一样的新对象出来,但是在Java中 赋值 = 是复制对象引用,假如我们想要得到一个对象的副本,使用赋值操纵是无法达到目的的。
看下面例子:
  1. Animal a1 = new Animal();
  2. a1.category = "人类";
  3. Animal a2 = a1;
  4. System.out.println(a1==a2);//true
  5. a2.category = "猫科动物";
  6. System.out.println(a1.category);//猫科动物
复制代码
显然,a1==a2返回为true,a1和a2指向的是同一个引用
并且对对象a2的属性值进行修改后,a1的属性值也跟着改变,因此这不是拷贝,要实现拷贝,须要用到Object对象的clone()方法,实现对对象中各个属性的复制,但它的可见范围是protected的
  1. protected native Object clone() throws CloneNotSupportedException;
复制代码

  • 所以实体类使用clone()方法的前提是:实现Cloneable接口,这是一个标志接口,自身没有方法,这算是一种约定。调用clone方法时,会去判定有没有实现Cloneable接口,没有实现Cloneable的话会抛异常CloneNotSupportedException。
  • 覆盖clone()方法,可见性提拔为public。
  1. public class Animal implements Cloneable {
  2.     String category;
  3.     @Override
  4.     protected Object clone() throws CloneNotSupportedException {
  5.         return super.clone();
  6.     }
  7.     public static void main(String[] args) throws CloneNotSupportedException {
  8.         Animal a1 = new Animal();
  9.         a1.category = "人类";
  10.         Animal a2 = (Animal) a1.clone();
  11.         System.out.println(a1 == a2);
  12.         a2.category = "猫科动物";
  13.         System.out.println(a1.category);
  14.     }
  15. }
  16. //输出:
  17. false
  18. 人类
复制代码
拷贝了一个新对象,与原对象引用不同,因此返回false,并且修改新对象的属性值,旧对象的属性值不改变
浅拷贝

当类中含有引用范例的属性时,新对象和旧对象的引⽤范例属性指向的是同⼀个对象,即为浅拷贝
  1. public class Animal implements Cloneable {
  2.     String category;
  3.     Person person;
  4.     @Override
  5.     protected Object clone() throws CloneNotSupportedException {
  6.         return super.clone();
  7.     }
  8.     public static void main(String[] args) throws CloneNotSupportedException {
  9.         Animal a1 = new Animal();
  10.         Person person = new Person("旧对象");
  11.         a1.category = "人类";
  12.         a1.person = person;
  13.         Animal a2 = (Animal) a1.clone();
  14.         a2.category = "新人类";
  15.         a2.person.name = "新对象";
  16.         System.out.println(a1.category + ":" + a1.person.name);//人类:新对象
  17.     }
  18. }
复制代码
如上,改变 a2中的引用范例 person.name = "新对象"后,a1的的引用范例 person.name 也发生了改变。
也就是说,新对象和旧对象的引⽤范例属性指向的是同⼀个对象,因此当改变新对象的引用范例(person)的属性值时,旧对象的引用范例的属性值也被改变。因此只是浅拷贝
这里可能就有疑问了,String范例也是引用范例,为什么进行了深拷贝,即如上所示,改变a2的属性值category,但却没有改变a1的category? String范例有点特殊,它本身没有实现Cloneable接口,故根本无法克隆,只能转达引用(注意:Java只有值转达,只是这里转达是原来引用所在值)。在clone()后,克隆后的对象开始也是指向的原来引用所在值(刚克隆完检查a1.category == a2.category 为true),但是一旦String的值发生改变(String作为不可更改的类——immutable class,在新赋值的时间,会创建了一个新的对象)就改变了克隆后对象指向的所在,让它指向了一个新的String所在,不会影响原对象的指向和值,原来的String对象还是指向的它自己的的所在。这样String在拷贝的时间就表现出了深拷贝的特点
深拷贝

当类中含有引用范例的属性时,新对象和旧对象的引⽤范例属性指向的不是同⼀个对象,即为深拷贝
要实现深拷贝,在clone方法中不仅调用了super.clone,而且须要调用Person对象的clone方法(Person也要实现Cloneable接口并重写clone方法),从而实现了深拷贝。
  1. public class Animal implements Cloneable {
  2.     String category;
  3.     Person person;
  4.     @Override
  5.     protected Object clone() throws CloneNotSupportedException {
  6.         Animal animal = null;
  7.         animal = (Animal) super.clone();
  8.         animal.person = (Person) person.clone();
  9.         return animal;
  10.     }
  11.     public static void main(String[] args) throws CloneNotSupportedException {
  12.         Animal a1 = new Animal();
  13.         Person person = new Person("旧对象");
  14.         a1.category = "人类";
  15.         a1.person = person;
  16.         Animal a2 = (Animal) a1.clone();
  17.         a2.person.name = "新对象";
  18.         System.out.println(a1.person.name);//旧对象
  19.     }
  20. }
复制代码
可以看到,新对象的引用范例 person 不会再受到旧对象的影响。
但是,在EffectiveJava中,反对使用clone方法来进行克隆,详情关注谨慎重写 clone 方法
回复

使用道具 举报

登录后关闭弹窗

登录参与点评抽奖  加入IT实名职场社区
去登录
快速回复 返回顶部 返回列表