深入理解Java中的clone对象

news2024/9/20 20:30:54

目录

1. 为什么要使用clone

2. new和clone的区别

3. 复制对象和复制引用的区别

4.浅克隆和深克隆

5. 注意事项


1. 为什么要使用clone

在实际编程过程中,我们常常遇到这种情况:有一个对象 A,需要一个和 A 完全相同新对象 B,并且此后对 B 任何改动都不会影响到 A 中的值,也就是说,A 与 B 是两个独立的对象,但 B 的初始值是由 A 对象确定的。在 Java 语言中,用简单的赋值语句是不能满足这种需求的。要满足这种需
求虽然有很多途径,但实现 clone()方法是其中最简单,也是最高效的手段。

2. new和clone的区别

new 

  • 通过调用类的构造函数来创建新对象实例,这意味着可以定义对象的初始化状态
  • 根据对象类型明确分配相应大小的内存空间
  • 需要显式初始化,通常在构造函数中完成
  • 涉及内存分配、构造函数调用和初始化,可能在复杂对象中相对较慢
  • 不依赖已有对象,每次创建都是全新的实例
  • 无需实现任何特殊接口
  • 用于全新创建对象实例,无历史状态需求
  • 生成完全独立的对象,互不影响

clone

  • 不会调用构造函数,而是复制已有对象的属性,适用于快速创建一个具有相似状态的对象
  • 分配内存给新对象,并且大小与原对象相同
  • 继承了原对象的初始化状态,不需要再次初始化
  • 通常认为较快,因为避免了构造函数调用和逐步初始化,但具体取决于JVM实现和场景
  • 必须有一个原对象作为克隆的基础
  • 需要实现Cloneable接口并重写clone()方法,否则会抛出CloneNotSupportedException异常
  • 用于基于现有对象快速生成一个副本,例如保存当前状态进行操作或比较
  • 虽然生成的是一个新对象,但如果涉及到引用类型字段,则需要深拷贝处理,否则可能存在依赖

        new操作符的本意是分配内存。程序执行到 new 操作符时,首先去看 new 操作符后面的类型,因为知道了类型,才能知道要分配多大的内存空间。分配完内存之后,再调用构造函数,填充对象的各个域,这一步叫做对象的初始化,构造方法返回后,一个对象创建完毕,可以把他的引用(地址)发布到外部,在外部就可以使用这个引用操纵这个对象。clone 在第一步是和 new 相似的,都是分配内存,调用 clone 方法时,分配的内存和原对象(即调用 clone 方法的对象)相同,然后再使用原对象中对应的各个域,填充新对象的域,填充完成之后,clone 方法返回,一个新的相同的对象被创建,同样可以把这个新对象的引用发布到外部,new主要用于创建全新的对象实例,而clone则用于基于已有对象快速生成副本。选择使用哪一种方式取决于具体的编程需求和场景。理解它们的不同之处能够帮助更好地掌握Java中的对象创建和管理技巧。

3. 复制对象和复制引用的区别

class Person {
    private String name;
    private int age;

    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }

    ...//此处省略set、get、toString方法
}

public class Main {
    public static void main(String[] args) {
        Person person1 = new Person("Alice", 30);
        Person person2 = person1; // 复制引用
        Person person3 = (Person)person1.clone(); // 复制对象

        System.out.println("Original Person: " + person1);
        System.out.println("Reference Copy: " + person2);
        System.out.println("Object Copy: " + person3);

        person1.setName("Bob");
        person1.setAge(40);

        System.out.println("After modification:");
        System.out.println("Original Person: " + person1);
        System.out.println("Reference Copy: " + person2); // 修改了person1,person2也会受到影响
        System.out.println("Object Copy: " + person3); // person3不受person1的修改影响
    }
}

在这个例子中,我们创建了一个Person类,并实例化了三个对象:person1person2person3。其中,person2是通过复制引用的方式创建的,而person3是通过复制对象的方式创建的。

当我们修改person1的属性时,可以看到以下结果:

  • person2person1的引用,因此它们的属性值相同,并且对其中一个对象的修改会影响另一个对象。
  • person3是通过复制person1的属性创建的新对象,因此它的属性值与person1相同,但对person3的修改不会影响person1

复制引用 

复制对象

4.浅克隆和深克隆

        在Java编程中,clone()方法提供了一种创建对象副本的方式。通过实现Cloneable接口并重写clone()方法,我们可以轻松地创建一个对象的完整复制品。然而,关于使用clone()方法,存在许多细节和注意事项,尤其是在处理浅克隆(shallow copy)与深克隆(deep copy)时。本篇博客将深入探讨这一主题。

        在Java中,所有对象都继承自Object类,其中包含一个clone()方法,但该方法是受保护的(protected),意味着我们不能直接在非子类外部调用它。为了使得clone()方法可用,我们需要实现Cloneable接口,该接口是一个标记接口(marker interface),本身不包含任何方法。

实现Cloneable接口后,我们可以通过重写clone()方法来提供自定义的复制逻辑。如果不重写clone()方法,对象将获得一个默认的浅复制行为,这意味着基本字段将被复制,但引用类型字段将只复制引用,而不是引用的对象

浅克隆

        浅复制是指在复制对象时,只复制基本类型的字段和引用,但不复制引用的对象。这意味着,如果原对象中的引用类型字段被修改,复制的对象也会受到影响。

class ShallowCopyExample implements Cloneable {
    int intValue; // 基本类型字段
    String stringValue; // 引用类型字段

    public ShallowCopyExample clone() throws CloneNotSupportedException {
        return (ShallowCopyExample) super.clone();
    }
}

深克隆

        相对地,深复制会复制所有字段,包括引用的对象。这就需要我们自定义clone()方法以实现所需行为。

class DeepCopyExample implements Cloneable {
    int intValue; // 基本类型字段
    String stringValue; // 引用类型字段
    DeepCopyExample innerObject; // 引用的对象

    public DeepCopyExample clone() throws CloneNotSupportedException {
        DeepCopyExample deepCopy = (DeepCopyExample) super.clone();
        deepCopy.innerObject = new DeepCopyExample(); // 对内部对象进行深复制
        deepCopy.innerObject.intValue = this.innerObject.intValue;
        deepCopy.innerObject.stringValue = this.innerObject.stringValue;
        return deepCopy;
    }
}

        这段代码展示了一个名为DeepCopyExample的Java类,该类实现了Cloneable接口。这个类有三个字段:一个基本类型(int)字段intValue,一个引用类型(String)字段stringValue,以及一个引用的对象字段innerObject

        在这个类中,我们重写了clone()方法,以便实现深拷贝。深拷贝意味着当我们复制一个对象时,不仅复制对象的值,还会复制对象所引用的其他对象。这样,原始对象和克隆对象之间的所有引用都是独立的。

        在clone()方法中,首先调用super.clone()来创建一个新的DeepCopyExample对象。然后,我们为innerObject字段创建一个新的DeepCopyExample对象,并将原始对象的innerObject字段的值复制到新对象的相应字段中。最后,返回新创建的深拷贝对象。

        需要注意的是,如果innerObject字段中的DeepCopyExample对象也包含其他引用类型的字段,那么这些字段也需要进行深拷贝。在这个示例中,我们只处理了intValuestringValue字段的深拷贝

 

 

5. 注意事项

  • Cloneable接口的存在是为了标记对象可以被克隆。如果我们忘记实现这个接口,clone()方法会抛出CloneNotSupportedException异常。
  • 对于含有引用类型字段的对象,默认的clone()方法不会创建新的对象实例来复制这些字段,而是复制它们的引用,这可能导致意外的副作用。
  • 在实现深复制时,需要特别关注循环引用的情况,即一个对象直接或间接引用了自己。不妥善处理这种情况会导致无限递归和栈溢出错误。
  • 当对象层次结构很复杂时,手动实现深复制可能会很繁琐,此时可以考虑使用序列化或其他第三方库来实现深复制。

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

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

相关文章

【【通信协议之ARP的FPGA实现其一】】

通信协议之ARP的FPGA实现其一 介绍 ARP 协议分为 ARP 请求和 ARP 应答,源主机发起查询目的 MAC 地址的报文称为 ARP 请求,目的主机响应源主机并发送包含本地 MAC 地址的报文称为 ARP 应答。当主机需要找出这个网络中的另一个主机的物理地址时&#xff0…

点击化学 ,如何用最简单的试剂叠氮化修饰后用于Click Reaction?

“点击化学”这一术语由斯克里普斯研究所的K. B. Sharpless 于2001年首次提出,这是一类涉及碳-杂原子间 化学键形成的反应,该类反应具有收率高,选择性好的特 点。词条“点击”意为将分子片段拼接起来就像将安全带扣 环的两部分扣起来一样简单…

大学英语四六级报名照不通过的原因

大学英语四六级报名照不通过的原因 #英语四六级 #大学英语四六级 #大学英语四六级考试 #英语四六级报名照片 #英语四六级考试报名照片

数仓建模:数仓设计中的10个陷阱

目录 0 引言 1 主要内容 1.1 过于迷恋技术,而没有将重点放在业务需求和目标上 1.2 没有或无法找到一个有影响的、平易近人的、明白事理的高级管理人员作为数仓建设的发起人 1.3 将项目处理为一个巨大的持续多年的项目,而不是追求更容易管理的、虽然…

日光辐射系统室内太阳光模拟器

太阳光模拟器能够为实验室环境提供稳定可靠的光照环境,其作用相当于将自然太阳光“搬进”室内实验室。这对于研究太阳能电池、光伏材料及其他与太阳能相关的设备和材料性能至关重要。 1.氙灯灯泡功率:≥450W; 2.输出光束尺寸:≥22…

秃姐学AI系列之:实战Kaggle比赛:图像分类(CIFAR-10)

目录 准备工作 整理数据集 将验证集从原始的训练集中拆分出来 整理测试集 使用函数 图像增广 读取数据集 定义模型 定义训练函数 训练和验证数据集 对测试集进行分类并提交结果 准备工作 首先导入竞赛需要的包和模块 import collections import math import os i…

智能优化算法-樽海鞘优化算法(SSA)(附源码)

目录 1.内容介绍 2.部分代码 3.实验结果 4.内容获取 1.内容介绍 樽海鞘优化算法 (Salp Swarm Algorithm, SSA) 虽然名称中提到的是“樽海鞘”,但实际上这个算法是基于群体智能的一种元启发式优化算法,它模拟了樽海鞘(Salps)在海…

第67期 | GPTSecurity周报

GPTSecurity是一个涵盖了前沿学术研究和实践经验分享的社区,集成了生成预训练Transformer(GPT)、人工智能生成内容(AIGC)以及大语言模型(LLM)等安全领域应用的知识。在这里,您可以找…

Leetcode sql high frequency questions 50 (based)

high frequency SQL 50 (basic version) 高頻sql題目(Leetcode) 查詢 1757. 可回收且低脂的产品 Question 表:Products ---------------------- | Column Name | Type | ---------------------- | product_id | int | | low_fats | enum | | rec…

评测AI写毕业论文软件排行榜前十名的网站

在当今信息爆炸的时代,AI智能写作工具已经成为我们写作过程中的得力助手。特别是对于学术论文的撰写,这些工具不仅能够提高写作效率,还能帮助用户生成高质量的文稿。以下是五款值得推荐的AI智能写论文软件,其中特别推荐千笔-AIPas…

Mysql基础练习题 1729.求关注者的数量 (力扣)

编写解决方案,对于每一个用户,返回该用户的关注者数量。 #按 user_id 的顺序返回结果表 题目链接: https://leetcode.cn/problems/find-followers-count/description/ 建表插入语句: Create table If Not Exists Followers(us…

VMware Workstation Pro 17 下载教程(Window环境)

自从24年中旬,博通公司以 610 亿美元收购的 VMware 宣布对其虚拟化软件套件进行一些重大调整。Windows 和 Linux 版 VMware Workstation Pro 和 Mac 版 VMware Fusion 不再需要个人使用许可证,也就是对个人用户免费。 1. 下载方式 Windows 和 Linux 版…

IDEA 编译运行gradle项目

IDEA 编译运行gradle项目 本文介绍Gradle 的三种安装方式 1.IDEA 编译自动安装gradle【推荐】 2.mac brew 安装 gradle 3.手动 安装 gradle IDEA 编译gradle项目,之前的项目都是maven管理,今天遇到一个sping boot 的项目是用gradle管理的,下面…

[Linux]:文件(下)

✨✨ 欢迎大家来到贝蒂大讲堂✨✨ 🎈🎈养成好习惯,先赞后看哦~🎈🎈 所属专栏:Linux学习 贝蒂的主页:Betty’s blog 1. 重定向原理 在明确了文件描述符的概念及其分配规则后,我们就可…

C程序设计(7.0安徽专升本函数)

在之前我很多代码执行都是放在函数里的,这样方便我管理和演示,现在能和大家更好的去了解函数了 考纲教材关于这个的理论知识太多了,且废话占大多数,甚至有些对于小白很晦涩难懂或容易搞混!所以在这我就尽量缩减理论知…

C语言——希尔排序

希尔排序是对于插入排序的一种优化 代码&#xff1a; #include <stdio.h> #include <stdlib.h> void shell_sort(int* p, int len) { int i; int j; int step; int tmp; for (step len / 2; step > 0; step step / 2) { fo…

手把手教你实现微信小程序定位

实现小程序的定位 框架&#xff1a;uniappvue 1&#xff0c;用户授权配置 在pages.json文件中配置 "pages": [{"path": "pages/home/index","style": {"navigationBarTitleText": "首页","navigationBarBac…

监控-zabbix

1运维监控 是指对计算机系统、网络、服务器等关键IT基础设施进行实时监控&#xff0c;以确保系统的稳定运行和及时发现潜在问题 2老监控框架&#xff08;不会用但需要知道&#xff09; Cacti&#xff1a; Cacti是一款基于PHP、MySQL开发的网络流量监测图形分析工具。主要监…

CSP-J算法基础 计数排序

文章目录 前言计数排序计数排序的过程总结 代码实现计数排序总结 前言 计数排序 计数排序&#xff08;Counting Sort&#xff09;是一种线性时间复杂度的排序算法&#xff0c;适用于范围有限的整数排序。它通过计数每个值出现的次数&#xff0c;依次排列这些值。该算法不通过比…

LVGL 控件之线条(lv_line)

目录 一、概述二、线条1、设置连接点2、自适应大小3、翻转 y 轴4、样式4.1 设置宽度4.2 末端形态 4.3 虚线5、API 函数 一、概述 线条部件只有一个组成部分&#xff1a;主体 LV_PART_MAIN。 通过一组点绘制出相连的直线&#xff0c;通过 lv_line_create 创建相应的对象。 二…