深浅克隆浅入浅出

2023-10-10 18:59
文章标签 克隆 深浅 浅入 浅出

本文主要是介绍深浅克隆浅入浅出,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

深浅克隆,浅入浅出

文章目录

  • 深浅克隆,浅入浅出
    • 浅克隆
    • 深克隆
      • 深克隆方式一:序列化
      • 深克隆方式二:所有引用类型都实现克隆
    • 相关问题
      • 1、使用克隆有什么好处?
      • 2、如何实现浅克隆?
      • 3、深克隆一般如何实现?有几种实现方法
      • 4、为什么不直接使用Object的Clone方法,还要重写之后才能克隆。

​ 这阵子在项目的改Bug阶段,在深浅克隆方法踩了一些坑,这些都是基础,需要我牢记的地方,以后踩坑的时候,可以看一下,也希望可以帮助到大家 。

​ 我们先来看一段代码;

public class CloneTest {public static void main(String[] args) {//等号赋值int number = 666;int number2 = number;//修改number2的值number2 = 888;System.out.println("number:"+number);System.out.println("number2"+number2);}
}

​ 程序的输出结果:

number:666
number2:888

可以看到,我们在使用基本类型的引用的时候,被引用的值,不会随着引用变量的改变而进行改变,但是,如果我们引用的不是基本类型呢?而是实例呢?我们来创建一个Cat的实例,如下:

import lombok.Data;@Data
public class Cat {private String name;private Integer age;
}

​ 实例之间的引用代码:

public class CloneTest {public static void main(String[] args) {Cat cat = new Cat();cat.setAge(6);cat.setName("加菲猫");//实例之间的引用Cat cat2 = cat;//修改cat2的属性cat2.setName("英短");cat2.setAge(1);System.out.println("Cat1:"+cat);System.out.println("Cat2:"+cat2);}
}

​ 代码输出结果:

Cat1:Cat(name=英短, age=1)
Cat2:Cat(name=英短, age=1)

可以看到,实例之前的引用,被引用实例,会根据引用实例的属性,而改变自身的属性 因为我们使用=赋值的是实例的内存地址,改变其中一个实例的属性,内存中就会产生变化, 因为Cat和Cat2引用的是同一个内存地址,所以就会出现这样的现象。为了预防这样的事情发生,就要使用对象克隆来解决这样的问题。

浅克隆

​ 浅克隆的默认实现方法是clone(),实现代码如下:

​ 首先,我们要在克隆的实例中,实现 Cloneable 接口,并重写 clone() 方法,如下

@Data
public class Cat implements Cloneable {private String name;private Integer age;@Overrideprotected Object clone() throws CloneNotSupportedException {return super.clone();}
}

​ 接下来,我们就可以修改一下代码,在实例之间引用的时候,使用clone方法来引用。如下:

public class CloneTest {public static void main(String[] args) throws CloneNotSupportedException {Cat cat = new Cat();cat.setAge(6);cat.setName("加菲猫");//实例之间的引用Cat cat2 = (Cat)cat.clone();//修改cat2的属性cat2.setName("英短");cat2.setAge(1);System.out.println("Cat1:"+cat);System.out.println("Cat2:"+cat2);}
}

​ 代码输出结果:

Cat1:Cat(name=加菲猫, age=6)
Cat2:Cat(name=英短, age=1)

以上这种复制方式叫做浅克隆。

浅克隆的实现条件:

需要克隆的对象必须实现 Cloneable 接口,并重写 clone() 方法,即可实现对此对象的克隆。

然后,在使用仙客隆的时候也会存在一个问题,如下:

​ 首先,我们在Cat类中添加一个引用类型的属性。代码如下:

@Data
public class CatChild {private String name;
}
@Data
public class Cat implements Cloneable {private String name;private Integer age;private CatChild catChild;@Overrideprotected Object clone() throws CloneNotSupportedException {return super.clone();}
}

​ 我们在使用浅克隆的时候,对象的属性中如果有引用类型的属性,那么是否能够克隆呢?我们来看以下代码:

public class CloneTest {public static void main(String[] args) throws CloneNotSupportedException {CatChild catChild = new CatChild();catChild.setName("猫儿子");Cat cat = new Cat();cat.setName("猫妈");cat.setCatChild(catChild);//复制CatCat cat2 = (Cat) cat.clone();cat2.setName("修改之后的猫妈");cat2.getCatChild().setName("修改之后的猫儿子");System.out.println("Cat1 name : "+cat.getName());System.out.println("Cat2 name : "+cat2.getName());System.out.println("Cat1 CatChnild name : "+cat.getCatChild().getName());System.out.println("Cat2 CatChnild name : "+cat2.getCatChild().getName());}
}

我们来看一下输出结果:

Cat1 name : 猫妈
Cat2 name : 修改之后的猫妈
Cat1 CatChnild name : 修改之后的猫儿子
Cat2 CatChnild name : 修改之后的猫儿子

从上面我们可以得出结论,浅克隆只会复制对象属性的类型,不会复制对象的引用类型。

在这里插入图片描述

如果要处理引用类型不被克隆的问题,我们就要用到深克隆

深克隆

​ **定义:**深克隆是对实例的引用属性也进行克隆,包括值类型和引用类型

深克隆的实现方式通常有以下两种:

序列化实现深克隆

​ 先将原对象序列化都内存的字节流中,再从字节流中反序列化出刚刚存储的对象,这个新对象和原对象不存在任何内存地址上的共享,这样就实现了深克隆

所有类型都实现克隆:

​ 实例中所有的引用类型都要实现 Cloneable 接口,并重写clone()方法,所有对象都是克隆的新对象,从而实现了深克隆

深克隆方式一:序列化

​ 实现思路:先将要拷贝对象写入到内存的字节流中,然后再从这个字节流中读出刚刚存储的信息,作为一个新对象返回,那么这个新对象和老对象就不存在内存地址上的相同,从而实现了深拷贝。**需要注意的是,实现拷贝的所有类,都要实现 Serializable 接口 ** ,我们需要写一个CloneUtils 拷贝工具类,参考代码如下:

​ 首先,两个实例实现Seralizable接口

@Data
public class Cat implements Serializable {private String name;private Integer age;private CatChild catChild;
}
@Data
public class CatChild implements Serializable {private String name;
}

​ 然后我们开始编写CloneUtils工具类

public class CloneUtils {public static <T extends Serializable> T clone(T obj) {T cloneObj = null;try {//写入字节流ByteArrayOutputStream bo = new ByteArrayOutputStream();ObjectOutputStream oos = new ObjectOutputStream(bo);oos.writeObject(obj);oos.close();//分配内存,写入原始对象,生成新对象ByteArrayInputStream bi = new ByteArrayInputStream(bo.toByteArray());//获取上面的输出字节流ObjectInputStream oi = new ObjectInputStream(bi);//返回生成的新对象cloneObj = (T) oi.readObject();oi.close();} catch (Exception e) {e.printStackTrace();}return cloneObj;}}

写完之后,我们来实验以下,是否进行了深克隆,很简单,将于原来的代码中.clone方法改为使用工具类进行克隆。

public class CloneTest {public static void main(String[] args) throws CloneNotSupportedException {CatChild catChild = new CatChild();catChild.setName("猫儿子");Cat cat = new Cat();cat.setName("猫妈");cat.setCatChild(catChild);//复制CatCat cat2 = CloneUtils.clone(cat);cat2.setName("修改之后的猫妈");cat2.getCatChild().setName("修改之后的猫儿子");System.out.println("Cat1 name : "+cat.getName());System.out.println("Cat2 name : "+cat2.getName());System.out.println("Cat1 CatChnild name : "+cat.getCatChild().getName());System.out.println("Cat2 CatChnild name : "+cat2.getCatChild().getName());}
}

输出结果为:

Cat1 name : 猫妈
Cat2 name : 修改之后的猫妈
Cat1 CatChnild name : 猫儿子
Cat2 CatChnild name : 修改之后的猫儿子

深克隆方式二:所有引用类型都实现克隆

​ 第二个方式 就是所有的实例都实现 Cloneable 接口 ,以我们的Cat和CatChild为例子,如下

@Data
public class CatChild implements Cloneable {private String name;@Overrideprotected Object clone() throws CloneNotSupportedException {return super.clone();}
}
@Data
public class Cat implements Cloneable {private String name;private Integer age;private CatChild catChild;@Overrideprotected Object clone() throws CloneNotSupportedException {return super.clone();}
}

在测试的代码中,我们取消使用工具类进行克隆,使用实例中clone方法进行克隆

public class CloneTest {public static void main(String[] args) throws CloneNotSupportedException {CatChild catChild = new CatChild();catChild.setName("猫儿子");Cat cat = new Cat();cat.setName("猫妈");cat.setCatChild(catChild);//复制CatCat cat2 = (Cat) cat.clone();cat2.setName("修改之后的猫妈");cat2.getCatChild().setName("修改之后的猫儿子");System.out.println("Cat1 name : "+cat.getName());System.out.println("Cat2 name : "+cat2.getName());System.out.println("Cat1 CatChnild name : "+cat.getCatChild().getName());System.out.println("Cat2 CatChnild name : "+cat2.getCatChild().getName());}
}

输出结果:

Cat1 name : 猫妈
Cat2 name : 修改之后的猫妈
Cat1 CatChnild name : 修改之后的猫儿子
Cat2 CatChnild name : 修改之后的猫儿子

相关问题

1、使用克隆有什么好处?

使用方便:

​ 加入要复制一个对象,但是这个对象中的部分属性已经被修改过了,如果不使用克隆的话,需要手动new一个对象并给属性赋值,相比克隆麻烦很多

性能:

​ 查看clone方法可以指定,他是native方法,这个方法是操作比较底层的语言实现的,所以执行效率会高一些。

隔离性:克隆的属性可以保证对象之间的属性不相互影响。

2、如何实现浅克隆?

克隆的对象实现 Cloneable 接口,并重写 clone() 方法,就可以实现了。

3、深克隆一般如何实现?有几种实现方法

一般有以下两种:

​ 通过序列化实现深克隆,Java原生远离恶化,Json序列化等

​ 所以引用类型都实现 Cloneable接口

4、为什么不直接使用Object的Clone方法,还要重写之后才能克隆。

虽然所有类都是Object的子类,但因为Object中的 clone() 方法被生命为protected访问级别,所有被Java.lang保下的类是不能直接使用的,因为要实现克隆,必须实现 Cloneable,并重写 Clone() 方法。

这篇关于深浅克隆浅入浅出的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/182426

相关文章

C#原型模式之如何通过克隆对象来优化创建过程

《C#原型模式之如何通过克隆对象来优化创建过程》原型模式是一种创建型设计模式,通过克隆现有对象来创建新对象,避免重复的创建成本和复杂的初始化过程,它适用于对象创建过程复杂、需要大量相似对象或避免重复初... 目录什么是原型模式?原型模式的工作原理C#中如何实现原型模式?1. 定义原型接口2. 实现原型接口3

ArcGIS Pro 克隆clone python环境报错问题处理方法

ArcGIS Pro 克隆clone python环境报错问题处理方法 (一)安装arcpro和深度学习安装包 首先安装arcgis pro桌面版和深度学习安装包后 然后克隆默认 Python 环境 arcgispro-py3 接下来,安装以下 Python 软件包:Tensorflow、fast.ai、Keras、Pytorch、Scikit-image、Pillow 和 Libtiff。 切

15天玩转小红书矩阵克隆自热打法的新手全攻略

专注前端流量,15天速通小红书自热矩阵打法,新手也能快速上手的小红书矩阵速通攻略,希望对所有看到这篇文章的新手小红书玩家有所帮助。 小红书自热克隆四要素之——足够的账号 矩阵自热的主要目的是通过足够多的矩阵账号来获得足够多的平台账号,那么账号就变的十分重要了。如何拥有足够多的账号就成为了玩转矩阵自热的基石。 账号渠道来源: 网厅:可以自己用身份证办理运营商的号卡,既可以使用流量卡也可以

如何在不重装的前提下,将1TB的硬盘克隆到500GB的固态硬盘?

借助傲梅分区助手,你能够在Windows 11、10、8、7中轻松的将1TB硬盘克隆到500GB固态硬盘。并且无需重新安装系统,即可轻松实现1TB机械硬盘升级至固态硬盘。 问:可以克隆到较小的固态硬盘吗? “大家下午好!我刚买了一块三星500 GB固态硬盘,目的是为了缩短启动时间和提高响应速度,因此想替换旧的1TB机械硬盘。不过,我不想在新的固态硬盘上重新安装Windows 系统和所有程序。所

hadoop实战(一) vmware下克隆多个ubuntu18.04服务器并开启ssh远程登录

一、 系统安装    因为使用虚拟机搭建集群,同一台电脑上需要启动多个服务器,因此对电脑负载比较大,因此选用server版本的iso,不必要的功能尽量不安装。可以去以下地址下载 https://www.ubuntu.com/download/server   安装完之后需要先更新一下,sudo apt-get update,然后在虚拟机–>管理–>克隆,复制三台。    复制完成之后几台

推荐Windows系统克隆软件!

本文向你推荐了一款简单好用的系统克隆软件,可轻松将系统从一个磁盘克隆至另一个磁盘,该软件支持 Windows11、10、8、7 和 XP 系统。 可以克隆操作系统吗? “我当前电脑上安装的是Windows 10 系统,但不知道硬盘出了什么问题,经常有异响,所以我买了一个新的固态硬盘,希望将系统从旧硬盘克隆到新硬盘上。但我之前从没试过将操作系统从一个磁盘克隆到另一个磁盘,现在不知道要怎么操作

java核心卷I学习笔记(四)——接口,Cloneable接口、克隆

1、接口(interface) A、接口的主要是用来描述类具备什么功能,但并不给出每个功能的实现。一个类可以实现(implement)或者多个接口,并在需要的时候实现相对应的接口的对象。 接口不是类,而是对类的一组需求描述,这些类要遵从接口描述的统一格式进行定义。 注:接口的作用类似与插座生产商,只生产统一的标准插座接口,而需要什么的灯具、生产什么样的灯具都会有极大的灵活性;即使有一天灯具坏

请解释Java中的对象克隆机制,并讨论浅拷贝和深拷贝的区别。什么是Java中的封装?请举例说明如何通过封装实现数据隐藏和访问控制。

请解释Java中的对象克隆机制,并讨论浅拷贝和深拷贝的区别。 在Java中,对象克隆机制允许你创建一个已经存在的对象的一个完全相同的副本。这种机制主要依赖于Object类的clone()方法,但是需要注意的是,Object类中的clone()方法是受保护的,这意味着它不能直接被子类使用,除非子类显式地覆盖这个方法并声明为public。 对象克隆的两种类型 Java中的对象克隆主要分为两种类型

Kaggle克隆github项目+文件操作+Kaggle常见操作问题解决方案——一文搞定,以openpose姿态估计项目为例

文章目录 前言一、Kaggle克隆仓库1、克隆项目2、查看目录 二、安装依赖三、文件的上传、复制、转移操作1.上传.pth文件到input目录2、将权重文件从input目录转移到工作目录 三、修改工作目录里的文件内容1、修改demo_camera.py内容 四、运行! 前言 想跑一些深度学习的项目,但是电脑没有显卡,遂看向云服务器Kaggle,这里可以每周免费使用30h的GP

centos7 克隆后的虚拟机配置过程

1.删除配置文件中的UUID那一行,vim /etc/sysconfig/network-scripts/ifcfg-ens33   <确保其中的ONBOOT=yes> 2.进入/etc/udev/rules.d/这个目录,删除文件70-persistent-ipoib.rules,rm -f 70-persistent-ipoib.rules 3.重启网络:service ne