JAVA克隆

news2024/11/27 18:37:34

在这里插入图片描述
更多精彩

先案例后讲解,这里是代码教父,今天讲解JAVA中的clone

目录

  • 什么是clone
  • 如何实现clone
    • 浅克隆
    • 深克隆
    • 小结
  • 什么时候使用clone
  • clone 相关类库的实现分析

什么是clone

在Java中,克隆(Clone)指的是创建一个现有对象的副本。这个副本将是一个全新的对象,但是它的属性值与原始对象相同。

如何实现clone

Java提供了clone()方法来实现对象的克隆。要使用clone()方法进行克隆操作,需要满足以下条件:

  1. 对象的类必须实现Cloneable接口,该接口是一个标记接口,表示该类可以被克隆。
  2. clone()方法必须在类中被重写为public访问修饰符,并且返回类型应该与类本身兼容。

默认情况下,clone()方法执行的是浅拷贝(Shallow Copy),它复制对象的字段值。如果对象中包含其他对象的引用,那么克隆对象和原始对象将共享这些引用,因此修改其中一个对象的引用将影响到另一个对象。

如果需要实现深拷贝(Deep Copy),也就是复制对象及其引用的所有对象,就需要在clone()方法中手动实现对其他对象的克隆操作。

下面请看例子:

浅克隆

public class CloneClassExample implements Cloneable{
    /**
     * 字符串
     */
    private String str;
    /**
     * Map 对象
     */
    private Map<String, String> map;

    public CloneClassExample(String str, Map<String, String> map) {
        this.str = str;
        this.map = map;
    }

    @Override
    public Object clone() throws CloneNotSupportedException {
        return super.clone();
    }

    @Override
    public String toString() {
        return "CloneClassExample{" +
                "str='" + str + '\'' +
                ", map=" + map +
                '}';
    }
}

class MainClass{
    public static void main(String[] args) {
        HashMap<String, String> map = Maps.newHashMap();
        map.put("key1","value1");
        map.put("key2","value1");
        CloneClassExample cloneClassExample = new CloneClassExample("A", map );
        try {
            CloneClassExample clone = cloneClassExample.clone();
            System.out.println("原来对象 \t" + cloneClassExample);
            System.out.println("clone对象 \t" + clone);
        } catch (CloneNotSupportedException e) {
            e.printStackTrace();
        }
    }
}

输出结果

原来对象 	CloneClassExample{str='A', map={key1=value1, key2=value1}}
clone对象 	CloneClassExample{str='A', map={key1=value1, key2=value1}}
  • 浅克隆存在问题

    • 如果对象中包含其他对象的引用,那么克隆对象和原始对象将共享这些引用,因此修改其中一个对象的引用将影响到另一个对象

      也就是说,如果我们改变了cloneClassExamplemap的值,相对应的clone中的map值也会变,现在来演示下这种问题

	public static void main(String[] args) {
        HashMap<String, String> map = Maps.newHashMap();
        map.put("key1","value1");
        map.put("key2","value1");
        CloneClassExample cloneClassExample = new CloneClassExample("A", map );
        try {
           CloneClassExample clone = cloneClassExample.clone();
            System.out.println("原来对象 \t" + cloneClassExample);
            System.out.println("clone对象 \t" + clone);
            // 改变cloneClassExample 中map的值
            cloneClassExample.getMap().put("changeKey1","changeVal1");
            System.out.println("change后原来对象 \t" + cloneClassExample);
            System.out.println("clone对象 \t" + clone);
        } catch (CloneNotSupportedException e) {
            e.printStackTrace();
        }

    }

输出结果

原来对象 	CloneClassExample{str='A', map={key1=value1, key2=value1}}
clone对象 	CloneClassExample{str='A', map={key1=value1, key2=value1}}
change后原来对象 	CloneClassExample{str='A', map={key1=value1, key2=value1, changeKey1=changeVal1}}
clone对象 	CloneClassExample{str='A', map={key1=value1, key2=value1, changeKey1=changeVal1}}

要解决这个问题,只能使用深克隆,下面上例子

深克隆

  • 还是以上面的例子为例,只需要重写上clone方法
	@Override
    public CloneClassExample clone() throws CloneNotSupportedException {
        CloneClassExample clone = (CloneClassExample) super.clone();
        clone.setMap(Maps.newHashMap(map));
        return clone;
    }
  • 再次执行main方法输出
原来对象 	CloneClassExample(str=A, map={key1=value1, key2=value1})
clone对象 	CloneClassExample(str=A, map={key1=value1, key2=value1})
change后原来对象 	CloneClassExample(str=A, map={key1=value1, key2=value1, changeKey1=changeVal1})
clone对象 	CloneClassExample(str=A, map={key1=value1, key2=value1})

根据输出结果,可以发现,clone对象并没有随着原来对象引用的改变而改变

小结

要实现对象克隆,需要满足以下条件

  1. 对象的类必须实现Cloneable接口,该接口标识类可以被克隆。
  2. 在类中重写clone()方法,并修改方法的访问修饰符为public,返回类型为类本身。
  3. 如果需要对引用类型深克隆,需要在clone()方法中,对引用类型的属性进行深拷贝。

什么时候使用clone

使用对象克隆可以在以下情况下非常有用:

  1. 复制对象:当需要创建一个对象的独立副本时,可以使用克隆来快速复制对象,而不必手动复制每个属性。
  2. 缓存数据:在某些情况下,需要缓存一些数据,但是不希望改动缓存中的数据影响到原始数据。克隆可以用于将数据缓存到另一个对象中,从而保持缓存数据的独立性。
  3. 多线程环境:当多个线程需要访问相同的对象时,为了避免对原始对象进行并发修改,可以使用克隆来创建每个线程的独立副本。
  4. 框架和库设计:在设计框架或库时,克隆提供了一种方便的方式来复制对象,以实现更高级别的功能或满足特定的需求。

需要注意的是,克隆并不总是必需的,并且在某些情况下可能存在性能和安全性方面的问题。因此,在使用克隆时需要谨慎考虑,并根据具体的需求和情况来决定是否使用克隆。此外,为了实现正确和有效的克隆操作,必须保证被克隆的类实现了Cloneable接口,并正确地重写了clone()方法。

clone 相关类库的实现分析

值得一提的是,我们在使用对象复制的时候,常用的就是Spring的BeanUtils和,Apache Commons 的BeanUtils

  1. Apache Commons BeanUtils:Apache Commons BeanUtils提供了copyProperties()方法,用于将源对象的属性值复制到目标对象。它使用Java反射机制来访问对象的属性,并通过属性的getter和setter方法进行赋值。

    示例使用Apache Commons BeanUtils进行属性复制:

    import org.apache.commons.beanutils.BeanUtils;
    
    BeanUtils.copyProperties(source, target);
    

    建议使用Apache Commons BeanUtils的情况:

    • 需要进行简单的属性复制,不需要关注特定的转换逻辑。
    • 源对象和目标对象具有相同的属性名,且属性类型兼容。

    需要注意的是

    如果源对象和目标对象的属性是引用类型,复制操作只是将引用复制给目标对象,并不会创建新的实例

  2. Spring的BeanUtils:Spring的BeanUtils提供了copyProperties()方法,与Apache Commons BeanUtils类似,也是将源对象的属性值复制到目标对象。除了支持简单的属性复制,它还提供了更多的功能,如忽略Null值、类型转换、自定义转换器等。

    示例使用Spring的BeanUtils进行属性复制:

    import org.springframework.beans.BeanUtils;
    
    BeanUtils.copyProperties(source, target);
    

    建议使用Spring的BeanUtils的情况:

    • 需要进行属性复制,且有特定的转换需求。
    • 需要忽略源对象中的Null值属性。
    • 需要进行自定义的属性转换操作,如日期的格式转换。

使用建议:

  • 对于简单的属性复制,两者都可以使用,具体选择根据个人偏好和项目需求。
  • 如果项目已经使用了Spring,可以优先考虑使用Spring的BeanUtils,因为它提供了更多的功能和选项。
  • 如果项目没有使用Spring,或者只需要进行简单的属性复制,可以选择使用Apache Commons BeanUtils,因为它是一个独立的类库,不依赖于Spring框架。
  • 如果需要进行更复杂的属性转换和自定义逻辑,可以考虑通过编写自定义的转换器或使用其他专门的映射工具,如MapStruct、Dozer等。
  • 通常情况下,Spring的BeanUtils在执行属性复制时比Apache Commons BeanUtils更快, 这主要是Spring的BeanUtils内部增加了缓存机制、字段赋值、简化逻辑等操作

总之,根据具体需求和项目背景,选择适合的属性复制工具可以提高开发效率和代码可维护性。

更多内容,敬请关注:#公众号:代码教父

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/742234.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

linux命令与shell编程

文章目录 一、概念linux内存嵌入式嵌入式层次图判断小端和大端 二、linux系统操作命令ls查看cd 命令pwd命令touch 创建文件mkdir 创建目录chmod 修改权限man命令cp 拷贝mv 移动rm命令cat命令echo 命令tty命令->查看当前终端号clear 命令ldd命令 ->查看文件依赖哪些库prin…

make makefile

文章目录 make是一个命令makefile or Makefile是一个当前目录下的文件使用&#xff1a;生成可执行文件清理 作用依赖关系依赖方法make会自动推导makefile中的依赖关系栈式结构为什么清理的时候要make 加上clean?make后面可以直接跟要生成的可执行文件,指定名称的依赖关系和依赖…

Python第二天之容器学习

1.List 容器无非就增删改查 1.添加 name_list [aaa,bbb,ccc,ddd] name_list.append(b1) name_list.insert(1,xxx) print(name_list)append 是在后面追加 而insert是自己定义下表插入 name_list [aaa,bbb,ccc,ddd] name_list2 [qqq,222,111] name_list.extend(name_list…

python 面向对象编程

文章目录 前言如何理解面向对象编程在 python 中如何使用面向对象编程定义类创建对象self添加和获取对象属性添加属性类外添加属性类中添加属性 访问属性类外访问属性类中访问属性 魔法方法__ init __() 方法__ str __()方法__ del __() 方法 前言 大家好&#xff0c;前面我们…

吐血整理,自动化测试Yaml框架配置文件-深入详解(超细)

目录&#xff1a;导读 前言一、Python编程入门到精通二、接口自动化项目实战三、Web自动化项目实战四、App自动化项目实战五、一线大厂简历六、测试开发DevOps体系七、常用自动化测试工具八、JMeter性能测试九、总结&#xff08;尾部小惊喜&#xff09; 前言 YAML详解 YAML它…

上位机智能通信统一解决方案OPC应用

上位机应用开发中的通信需求 通信过程/通信协议多样性 通信统一化处理方案&#xff1a;OPC&#xff08;Open Platform Communications&#xff09;、OPC UA&#xff08;OPC Unified Architecture&#xff09; 基于西门子1500PLC的OPC服务器对接 internal class Program{stati…

BTP Integration Suite学习笔记 - (Unit1) Developing with SAP Integration Suite

今天决定跟着SAP官方资源系统学习一遍BTP Itegration Suite。找到两个Learning Journey: SAP Integration Suite Foundation 和 Solution Integration on SAP BTP。还有一个更大的roadmap,Integration Suite的官方内容在这里都可以链接到。 认证暂时只找到了一个比较基础的&…

干撸Spring,太难了!阿里P8级别「Spring源码全解析」带你起飞

有朋友跟我反映说&#xff0c;最近想详细学习Spring源码&#xff0c;可网上查到的文章都很一般&#xff0c;有没有大牛分享自己Spring实践与源码结合的方法&#xff1f; 干撸Spring&#xff0c;太难了&#xff01;难到你不知道从哪下手&#xff01; 为什么 Spring 天天用&…

云和DevOps如何帮助加速数字化转型?

1.云和 DevOps&#xff1a;概述 数字化转型已成为寻求在现代时代蓬勃发展的企业的一项关键举措。为了加速这一转型&#xff0c;组织正在利用云计算的力量并采用DevOps实践。云计算提供可扩展且灵活的基础架构&#xff0c;而 DevOps 则支持协作和持续交付的文化。本文将探讨云和…

ETHERCAT转PROFIBUS网关连接ethercat网线接口定义

远创智控YC-DP-ECT&#xff0c;是自主研发的一款PROFIBUS从站功能的通讯网关&#xff0c;它的主要功能是将ETHERCAT设备接入到PROFIBUS网络中。 YC-DP-ECT这个小小的网关可不简单&#xff0c; 连接到PROFIBUS总线中做为从站使用&#xff0c;连接到ETHERCAT总线中做为从站使用…

实现企业安全云转型的思考

如今大部分企业都在开展数字化数字化转型&#xff0c;云计算一直在其中起着主导地位&#xff0c;因而组织面临着双重挑战&#xff1a;如何将业务无缝迁移至云上&#xff0c;并确保这种转型的安全。 虽然云的使用保证了可扩展性、成本效率和生产力的提高&#xff0c;但上云过程…

【C++顺序容器】deque的成员函数和非成员函数

目录 deque 1. deque的成员函数 1.1 构造、析构和赋值运算符重载 1.1.1 构造函数 1.1.2 析构函数 1.1.3 赋值运算符重载 1.2 迭代器 1.3 容量 1.4 元素访问 1.4.1 遍历方法 1.5 修改器 1.6 空间配置器 2. deque的非成员函数 deque deque&#xff08;通常发音为 &…

告别VLAN孤岛,两招让你轻松实现互访

实际网络中&#xff0c;经常会有VLAN之间互访的需求。 很多网工通常会选择一些方法&#xff0c;来实现不同VLAN间主机的相互访问&#xff0c;比如说Vlanif、单臂路由。 今天就教你轻松实现VLAN间互访。 01-通过子接口实现 VLAN 间的互访 在二层交换环境下&#xff0c;一个VLA…

线程是什么

目录 一、教材观点 二、简述进程是如何运行的 简述进程切换原理&#xff1a; 三、线程是什么 线程底层是如何被管理的 四、重讲线程概念理解 一、教材观点 教材观点&#xff1a; 线程是一个执行分支&#xff0c;执行分支更细&#xff0c;调度成本更低。线程是进程内部的…

Android SystemServer 启动流程分析

和你一起终身学习&#xff0c;这里是程序员Android 经典好文推荐&#xff0c;通过阅读本文&#xff0c;您将收获以下知识点: 一、SystemServer 启动的服务有哪些二、SystemServer启动总体流程概述三、SystemServer 如何启动&#xff0c;是谁启动的&#xff1f;四、 SystemServe…

解决哈希冲突

1、HashMap引入了链式寻址法来解决hash冲突&#xff0c;冲突的key&#xff0c;HashMap把这些key组成一个单向链表&#xff0c;然后采用尾插法把key保存到链表的尾部。 使用二次扰动函数&#xff08;hash函数&#xff09;来降低哈希冲突的概率&#xff0c;使数据平均分布。 pu…

【VUE】Unterminated template literal:拼接字符串包括<script></script>时报错误

vue拼接字符串包括时报错误提示Unterminated template literal vue拼接字符串包括script标签时报错误提示Unterminated template literal解决方法加反斜杠就可以&#xff0c;在script结束标签里边加反斜杠<\/script> 代码 htmlData: <div id"b0e5c00cc51e4a4…

svg图标颜色随父元素变换

给svg加上一下样式 fill: currentColor;注&#xff1a;为什么有的svg设置了fill: currentColor;还是一样不生效&#xff1f; 用电脑自带的txt或者代码开发工具&#xff08;如&#xff1a;HbuilderX&#xff09;打开svg文件 全局搜索 fill 把fill指定颜色去掉 保存就OK了

存储函数,存储过程的应用

查看触发器 mysql> show triggers \G *************************** 1. row ***************************Trigger: student_insert_triggerEvent: INSERTTable: studentStatement: BEGINupdate student_total set totaltotal1;ENDTiming: AFTERCreated: 2023-07-11 16:13:51…

msvcp71.dll丢失怎样修复,msvcp71.dll丢失的三个解决方法

在运行游戏&#xff0c;软件程序的时候&#xff0c;电脑提示msvcp71.dll丢失怎样修复&#xff1f;下面小编就开始介绍msvcp71.dll在电脑系统中的的目录&#xff0c;作用&#xff0c;以及丢失后三个解决方法。 目录 msvcp71.dll通常位于系统目录里&#xff1a; msvcp71.dll丢失…