Clonable接口以及再次理解深拷贝与浅拷贝!!(面试常考)

首先我们来思考一个问题:如何进行对象的拷贝?

Object 类中存在一个 clone 方法, 调用这个方法可以创建一个对象的 "拷贝". 但是要想合法调用 clone 方法, 必须要先实现 Clonable 接口, 否则就会抛出 CloneNotSupportedException 异常.

我们先来看Clonable接口的源码:

在这里插入图片描述

我们可以看到这类接口什么都没有,那么我们称这类接口为空接口,也就是标记接口.

空接口的意义:标记当前这个类可以被克隆的.克隆会做一件事情,拷贝一份副本.

同时关于对象的拷贝会牵扯出之前我们所提到的深拷贝和浅拷贝的问题,下面我们来进行对比:

Clonable接口结合深拷贝问题与浅拷贝问题

======================================================================================

首先一个对象的引用如果想要进行克隆,那么其对应的类必须实现Clonable 接口,并且重写Object类中的clone方法,并且重写的clone方法后必须抛出CloneNotSupportedException 异常.

Clonable接口结合深拷贝


我们直接通过代码来进行解析:先来看一段代码:

1.class Person implements Cloneable {

  1. public int age;

  2. @Override

  3. //此处的clone方法必须抛出CloneNotSupportedException异常

  4. protected Object clone() throws CloneNotSupportedException {

  5.    return super.clone();  
    
  6. }

8.}

9.//此处的主函数也需要抛出这个异常

  1. public static void main(String[] args) throws CloneNotSupportedException {

  2.    Person person = new Person();  
    
  3.    /*注意对于下面的调用clone方法有两点需要注意: 
    
  4.    1:首先引用在调用clone方法时main方法需要抛出CloneNotSupportedException异常 
    
  5.    2:因为clone方法的返回值为引用类型Object,所以如果是其他类定义的引用就需要强制类型转换 
    
  6.     */  
    
  7.    Person person1 = (Person) person.clone();  
    
  8.    System.out.println(person.age);  
    
  9.    System.out.println(person1.age);  
    
  10.    System.out.println("===========对比===========");  
    
  11.    person1.age = 10;  
    
  12.    System.out.println(person.age);  
    
  13.    System.out.println(person1.age);  
    
  14. }

此段代码的输出结果为:

在这里插入图片描述

此时我们会发现首先我们对person所指向的对象进行了拷贝,并把它赋给了一个新的引用person1,那么此时在内存是什么样的呢?我们来看下图所示:

在这里插入图片描述

此时我们可以看到通过clone方法我们在堆上克隆了一个副本,并将这个副本对象赋给了一个新的引用,并且当我们修改person1这个引用所指向对象中所包含的简单类型age的值时,person这个引用所指向对象中所包含i的简单类型age的值并没有发生改变,那么这种情况我们便称之为深拷贝即当拷贝结束后,通过一个新的引用修改所拷贝的新的对象的其中的某个类型的值时,并不影响原来引用所对应的相同对象中的相同类型的值,那么此时便为深拷贝

Clonable接口结合浅拷贝


下面我们来介绍浅拷贝:

因为之前堆上的对象内部存储都是简单类型,所以都是深拷贝,那么如果存储引用类型后,就会发生浅拷贝了,下面来看代码:

1.class Money {

  1. double money = 12.6;

3.}

5.class Person implements Cloneable {

  1. public int age;

  2. Money m = new Money();

  3. @Override

  4. //此处的clone方法必须抛出CloneNotSupportedException异常

  5. protected Object clone() throws CloneNotSupportedException

必看视频!获取2024年最新Java开发全套学习资料 备注Java

{

  1.    return super.clone();  
    
  2. }

14.}

16.public class TestDemo {

  1. public static void main(String[] args) throws CloneNotSupportedException {

  2.    Person person = new Person();  
    
  3.    Person person1 = (Person) person.clone();  
    
  4.    System.out.println(person.m.money);  
    
  5.    System.out.println(person1.m.money);  
    
  6.    System.out.println("===========对比===========");  
    
  7.    person1.m.money = 99.9;  
    
  8.    System.out.println(“修改后为”+person.m.money);  
    
  9.    System.out.println(“修改后为”+person1.m.money);  
    
  10. }

27.}

此时我们新建了一个Money类,并在Person类中加入了Money类的实例,那么此时Person类的实例在堆上的存储中便会多出一个引用类型,那么来看下其在内存上的存储示意图吧:

在这里插入图片描述

此时我们会发现我们通过clone方法仅仅将简单类型age和引用类型m克隆了过来但是m所指向的对象我们并没有克隆过来,那么两者的m引用都将会指向同一个对象

此时如果我们通过person1.m.money去修改money的值为99.9时我们会发现原引用person所对应的money值也随之变成了99.9,那么这种现象我们称之为浅拷贝即其当一个新的引用去修改其克隆过来的对象中的某个类型(此段代码为引用类型)的值后,原引用调用这个类型时便会发现这个值为新修改后的值了,并不是原来的值

所以上述代码中的最终结果我们会发现当其中一个引用修改了值后,另一个引用去调用时也会得到相同的值,我们来看运行结果以证实我们的猜想吧

在这里插入图片描述

果然就算修改过后两者的值依然相等

将深拷贝改为浅拷贝的方法

此时就会有同学有疑问了,上述代码如何可以实现深拷贝呢?

答案非常简单:此时我们让Money类实现Cloneable接口就好。目的就是为了将Money类的实例也能通过clone方法拷贝过来,下面来看代码:

1.class Money implements Cloneable {

  1. double money = 12.6;

  2. @Override

  3. protected Object clone() throws CloneNotSupportedException {

  4.    return super.clone();  
    
  5. }

8.}

最近我根据上述的技术体系图搜集了几十套腾讯、头条、阿里、美团等公司21年的面试题,把技术点整理成了视频(实际上比预期多花了不少精力),包含知识脉络 + 诸多细节,由于篇幅有限,这里以图片的形式给大家展示一部分

clone();

  1. }

8.}

[外链图片转存中…(img-653cka5y-1716437698268)]

最近我根据上述的技术体系图搜集了几十套腾讯、头条、阿里、美团等公司21年的面试题,把技术点整理成了视频(实际上比预期多花了不少精力),包含知识脉络 + 诸多细节,由于篇幅有限,这里以图片的形式给大家展示一部分

[外链图片转存中…(img-LgaVlp0N-1716437698269)]

### Java 深拷贝浅拷贝的概念 在Java编程中,对象的复制操作可以分为两种类型:浅拷贝深拷贝。这两种类型的拷贝有着不同的行为模式。 #### 浅拷贝 (Shallow Copy) 浅拷贝创建一个新的对象,这个新对象会拥有原对象属性值的一个副本。如果这些属性指向其他对象,则不会创建新的对象;相反,两个对象将继续共享同一个引用的对象[^2]。 #### 深拷贝 (Deep Copy) 相比之下,深拷贝不仅复制原始对象本身,还会递归地复制所有被引用的对象,从而确保源对象及其内部组件完全独立于目标对象。这意味着更改其中一个对象的数据不会影响另一个对象的状态[^3]。 ### 实现方式对比 #### 浅拷贝实现 最简单的浅拷贝可以通过继承`Cloneable`接口并覆盖默认的`Object.clone()`方法来完成: ```java public class ShallowCopyExample implements Cloneable { private int value; private Object reference; @Override protected Object clone() throws CloneNotSupportedException { return super.clone(); } } ``` 这种方式只会复制基本数据类型成员变量,并且对于非静态字段中的对象引用仅做简单赋值处理,即两者仍然指向同一内存地址上的实例[^1]。 #### 深拷贝实现 有多种途径可达成真正的深拷贝效果,以下是几种常见做法: ##### 方法一:通过构造器 利用类自身的构造函数来进行初始化工作,在此过程中手动分配空间给每一个子元素,以此达到彻底分离的目的。 ```java public class DeepCopyConstructorExample { private final String name; private final List<String> items; public DeepCopyConstructorExample(String name, List<String> items) { this.name = name; this.items = new ArrayList<>(items); } // Constructor for deep copy public DeepCopyConstructorExample(DeepCopyConstructorExample other) { this(other.getName(), other.getItems()); } // Getters and setters... } ``` ##### 方法二:重写 `clone()` 方法 当希望保持原有类层次结构不变的情况下,可以在自定义类里面覆写父类提供的公共保护级别的克隆功能,同时注意要调用超类版本的方法以保证基类部分也能得到适当处理。 ```java @Override protected Object clone() throws CloneNotSupportedException { DeepCopyMethodTwo result = (DeepCopyMethodTwo)super.clone(); result.deepField = this.deepField != null ? this.deepField.clone() : null; // Assuming deepField is also clonable. return result; } ``` ##### 方法三:序列化机制 借助Java内置的序列化特性也是一种可行的选择——先将待复制实体转换成字节流形式保存到临时存储介质当中去(如文件或字节数组),再读取回来重建出全新的同构体出来即可满足需求。 ```java // Serialization-based approach omitted due to complexity of setup code required. ``` 以上就是有关Java深拷贝浅拷贝的主要知识点介绍以及具体实践方案说明[^4]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值