窥探 引用拷贝、浅拷贝、深拷贝 的那些事 (clone版)

news2025/1/20 22:03:57

谁家玉笛暗飞声

散入春风满洛城


往期回顾
内部类


目录

引用拷贝

介绍

总结

浅拷贝

介绍

浅拷贝的步骤

深拷贝

介绍

引用拷贝

介绍

引用拷贝就是我们常用的 “赋值” ,只是复制了原对象的引用,即两个对象指向同一块内存堆地址。修改其中的一个对象会影响到另一个对象

例子 ~

当我们想要复制一个对象时,最自然的操作就是将该对象直接赋值给另一个对象

如下图:Student s2 = s1

class Student{
    private String name;
    private int age;

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

    public Student() {
        this.name = "张三";
        this.age = 18;
    }

    public void StudentPrint(){
        System.out.println("name:"+name+",age:"+age);
    }
}

class Test1 {
    public static void main(String[] args) {
        Student s1 = new Student("李四",20);
        Student s2 = s1;
        s1.StudentPrint();
        s2.StudentPrint();
        System.out.println("========");
        System.out.println(s1);
        System.out.println(s2);
    }
}

输出的结果为:

name:李四,age:20
name:李四,age:20
========
Student@3d494fbf
Student@3d494fbf

根据打印结果我们可以知道:引用拷贝会生成一个新的对象引用地址,但是两个最终指向依然是同一个对象

 

总结

引用拷贝的优点在于能够快速的实现 “拷贝” (一个变量拥有另一个变量的所有属性)。缺点也很明显,由于所指向的是同一个对象,所以任意一个变量操作了对象的属性,都会影响另一个变量

思考

如何创建一个对象,将目标对象的内容复制过来而不是直接拷贝引用呢?

--- 这里就要介绍我们的深浅拷贝

浅拷贝

介绍

浅拷贝会创建一个新对象,这个对象有着原始对象属性值的一份精确拷贝

对于数据类型是基本数据类型的成员变量,浅拷贝会直接进行值传递,也就是将该属性值复制一份给新的对象。因为是两份不同的数据,所以对其中一个对象的该成员变量值进行修改,不会影响另一个对象拷贝得到的数据
对于数据类型是引用数据类型的成员变量,比如说成员变量是某个数组、某个类的对象等,那么浅拷贝会进行引用传递,也就是只是将该成员变量的引用值(内存地址)复制一份给新的对象。因为实际上两个对象的该成员变量都指向同一个实例。在这种情况下,在一个对象中修改该成员变量会影响到另一个对象的该成员变量值。

 

浅拷贝的步骤

Object 中已经有 clone() 克隆方法因为是 native 方法,底层已经实现了拷贝对象的逻辑,只要重写即可

注意一定要标注 Cloneable接口,说明这个类对象是可以被拷贝的

具体案例如下:

class Money{
    public double money;

    public Money() {
        this.money = 13.14;
    }

    public Money(double money) {
        this.money = money;
    }

    void MoneyPrint(){
        System.out.println(" Money: "+money);
    }
}


class Student implements Cloneable{
    public String name;
    public int age;
    public Money m = new Money();

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

    @Override
    protected Object clone(){
        try {
            return super.clone();
        }catch (CloneNotSupportedException e){
            throw new AssertionError();
        }
    }

    public void PrintStudent(){
        System.out.print("Student name: " + name+" age: " + age);
        m.MoneyPrint();
    }

}

class Test{
    public static void main(String[] args) {
        Student student = new Student("张三",18);
        student.PrintStudent();
        Student student1 = (Student) student.clone();
        student1.PrintStudent();
        student1.m.money  = 3.14;
        student1.name = "李四";
        student1.age = 20;
        System.out.println("=============");
        student1.PrintStudent();
        student.PrintStudent();
        System.out.println("=============");
        System.out.println(student.m);
        System.out.println(student1.m);
    }
}

这里再提一下写代码过程中的注意事项:
<1> 因为使用的是 Object 中的 clone(),编译器调用的时候会抛出异常,这里一定要写异常捕捉的·代码否则会报错

    @Override
    protected Object clone(){
        try {
            return super.clone();
        }catch (CloneNotSupportedException e){
            throw new AssertionError();
        }
    }

<2> 因为我们调用的是 Object 的克隆,所以类型是 Object,这里就会出现类型不匹配的问题,只需向下转型即可

Student student1 = (Student) student.clone();

输出结果:

Student name: 张三 age: 18 Money: 13.14
Student name: 张三 age: 18 Money: 13.14
=============
Student name: 李四 age: 20 Money: 3.14
Student name: 张三 age: 18 Money: 3.14
=============
com.thz.Money@70177ecd
com.thz.Money@70177ecd

通过以上结果以上结论的正确性:
我们的基础类型是两个不同的对象,而引用类型是共用一个对象(两个Money的地址相同)

 

思考

那么如何让浅拷贝进行的更彻底呢?-- 将共用的引用空间也进行拷贝,让两个对象互不干扰

深拷贝

介绍

深拷贝是一种完全拷贝,无论是基本类型还是引用类型都会完完全全的拷贝一份,在内存中生成一个新的对象。这样拷贝对象和被拷贝对象没有任何关系,互不影响

那么深拷贝该如何实现呢?

-- 我们可以在浅拷贝的基础上,对其内部的引用对象进行拷贝即可

深拷贝与通过重写 clone 方法实现浅拷贝的基本思路一样,只需要为对象图的每一层的每一个对象都实现 Cloneable 接口并重写 clone 方法,最后在最顶层的类的重写的 clone 方法中调用所有的 clone 方法即可实现深拷贝。简单的说就是:每一层的每个对象都进行浅拷贝就是深拷贝

案例如下:

class Money implements Cloneable {
    public double money;

    public Money() {
        this.money = 13.14;
    }

    public Money(double money) {
        this.money = money;
    }

    void MoneyPrint(){
        System.out.println(" Money: "+money);
    }

    @Override
    protected Object clone(){
        try {
            return super.clone();
        }catch (CloneNotSupportedException e){
            throw new AssertionError();
        }
    }
}


class Student implements Cloneable{
    public String name;
    public int age;
    public Money m = new Money();

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

    @Override
    protected Object clone(){
        try {
            Student tmp = (Student)super.clone();
            tmp.m = (Money)m.clone();
            return tmp;
        }catch (CloneNotSupportedException e){
            throw new AssertionError();
        }
    }

    public void PrintStudent(){
        System.out.print("Student name: " + name+" age: " + age);
        m.MoneyPrint();
    }

}

class Test{
    public static void main(String[] args) {
        Student student = new Student("张三",18);
        student.PrintStudent();
        Student student1 = (Student) student.clone();
        student1.PrintStudent();
        student1.m.money  = 3.14;
        student1.name = "李四";
        student1.age = 20;
        System.out.println("=============");
        student1.PrintStudent();
        student.PrintStudent();
        System.out.println("=============");
        System.out.println(student.m);
        System.out.println(student1.m);
    }
}

输出结果

Student name: 张三 age: 18 Money: 13.14
Student name: 张三 age: 18 Money: 13.14
=============
Student name: 李四 age: 20 Money: 3.14
Student name: 张三 age: 18 Money: 13.14
=============
com.thz.Money@70177ecd
com.thz.Money@1e80bfe8

通过以上案例可以知道:
我们在浅拷贝的基础上重写了引用对象 Moeny 的克隆方法,并且在 Student 的拷贝中调用了这个方法。此时两个对象的 Money 的地址就不同了,所以当一个类中所有每个成员都实现克隆,并且在最顶层的 clone
  中调用,那么就是深拷贝 ~

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

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

相关文章

【图灵完备 Turing Complete】游戏经验攻略分享 Part.6 处理器架构2 函数

新的架构来了&#xff0c;本游戏的最后一个攻略分享&#xff0c;最后汇编部分无非是对于操作码的熟练&#xff0c;硬件没有问题&#xff0c;那么也就无关痛痒了。 汇编实现&#xff0c;两数相或和两数相与非一起相与即可。 八位异或器&#xff0c;整就完事了。 有手就行。 利…

【梯度下降算法学习笔记】

梯度下降单参数求解 经过之前的学习我们来引入梯度下降的理念 α \alpha α为学习率 w 1 w 初 − α ∂ J ( w ) ∂ w w_1w_初-\alpha\frac{\partial J(w)}{\partial w} w1​w初​−α∂w∂J(w)​ w 2 w 1 − α ∂ J ( w 1 ) ∂ w 1 w_2w_1-\alpha\frac{\partial J(w_1)}…

国庆电影扎堆来袭,AI智能体帮你推荐必看佳片!(附制作教程)

大家好&#xff0c;我是凡人。 今天看到新闻&#xff0c;发现国庆有10部影片要扎堆儿上映&#xff0c;对于选择困难症的我属实有点难选&#xff0c;同时也想避开一些坑省的浪费金钱和时间。 本着不知道就问AI的习惯&#xff0c;想问问大模型怎么看&#xff0c;但做了简单的交…

MySQL 优化器:理解与探秘

在 MySQL 数据库的世界里&#xff0c;优化器扮演着至关重要的角色。它就像是一位幕后的魔法师&#xff0c;默默地为数据库的高效运行贡献着力量。那么&#xff0c;MySQL 优化器究竟是什么&#xff1f;它又是如何工作的呢&#xff1f;让我们一起来揭开它的神秘面纱。 一、MySQL…

行业人工智能研究-Python自监督方式学习图像表示算法

学术界人工智能研究落后于工业界 摘要 行业或工业界在人工智能研究上超出学术界&#xff0c;并占据着大量的计算力&#xff0c;数据集和人才诱人的薪水和明朗的预期吸引大量人才离开学术界&#xff0c;涌入行业或工业界即使&#xff0c;比如Meta开源其人工智能模型&#xff0…

二分查找算法(1) _二分查找_模板

个人主页&#xff1a;C忠实粉丝 欢迎 点赞&#x1f44d; 收藏✨ 留言✉ 加关注&#x1f493;本文由 C忠实粉丝 原创 二分查找算法(1) _二分查找模板 收录于专栏【经典算法练习】 本专栏旨在分享学习算法的一点学习笔记&#xff0c;欢迎大家在评论区交流讨论&#x1f48c; 1. 二…

掌握Android开发新趋势:Jetpack与现代架构技术全解析

随着Android开发技术的不断进步&#xff0c;Jetpack和现代架构技术已成为构建高效、可维护应用的关键。本文将为您介绍一套全面的学习资料&#xff0c;包括大纲、PDF文档、源代码以及学习视频&#xff0c;帮助您深入理解Jetpack核心库、架构组件以及现代开发工具。 内容&#…

linux-基础知识4

网络连接性测试 ping ping可以用来测试本机与目标主机的连通速度网络稳定性 ping -c 5 -s 1024 目标主机ip地址 -c 表示ping包的个数,linux如果缺省-c会一直ping下去&#xff0c;windows平台的选项是-n -s指定ping发送数据的字节数默认是84字节。windows的是-l 没有问题时会之…

如何设计出一个比较全面的测试用例

目录 1. 测试用例的基本要素(不需要执行结果) 2. 测试用例的给我们带来的好处 3. 用例编写步骤 4. 设计测试用例的方法 4.1 基于需求进行测试用例的设计 4.2 具体的设计方法 1.等价类 2.边界值 3.判定表&#xff08;因果图&#xff09; 4.正交表法 5.场景设计法 6.错误猜测…

IO流体系(FiletOutputStream)

书写步骤&#xff1a; 1.创建字节输出流对象 细节1:参数是字符串表示的路径或者是File对象都是可以的 细节2:如果文件不存在会创建一个新的文件&#xff0c;但是要保证父级路径是存在的。 细节3:如果文件已经存在&#xff0c;则会清空文件 2.写数据 细节:write方法的参数…

Python | 绘制核密度图

写在前面 台风天&#xff0c;适合敲代码。前两天正好看到一个有意思的核密度图&#xff0c;使用的是seaborn绘制的。之前了解过这个包&#xff0c;但是一致没有去绘制相关的图&#xff0c;这次正好去学习一下相关的函数。 绘制结果如下所示&#xff1a; 主要两个有意思的地方…

二维光场分析

一、单色光波长的复振幅表示 实波函数 复波函数 复振幅 由于时间因子相同,可以用复振幅来描述 光强 1.1 球面波的复振幅(单色点光源发出的光波) 等相位面是同心球面,波矢处处与等相位面垂直,即 是 r = 1 处的振幅 发散球面波: 会聚球面波: <

828华为云征文 | 华为云X实例监控与告警管理详解

前言 随着云计算的飞速发展&#xff0c;越来越多的企业将业务部署在云平台上&#xff0c;云服务器实例的管理变得尤为重要。云实例的稳定性、性能及安全性&#xff0c;直接影响着业务的连续性与用户体验。为了确保这些目标的实现&#xff0c;监控与告警是关键手段。本文将详细…

2024华为杯研赛C题原创python代码+结果表(1-2问)|全部可运行|精美可视化

2024华为杯研赛C题原创python代码结果表&#xff08;1-2问&#xff09;&#xff5c;全部可运行&#xff5c;精美可视化 以下均为python代码&#xff0c;推荐用anaconda中的notebook当作编译环境 第一问&#xff1a; import pandas as pd import numpy as np import matplotl…

pg入门18—如何使用pg gis

1. 下载postgre gis镜像 2. 运行镜像 docker run -p 15432:5432 -d -e POSTGRES_PASSWORDAb123456! postgis/postgis:12-3.4-alpine 3. 使用gis # 进入容器&#xff0c;登录pgdocker exec -it bash# 登录数据库psql -U postgres# 创建数据库CREATE DATABASE mygeotest;# 使用…

初步认识C++模版

前言 在C语言中&#xff0c;我们知道函数的形参需要指定类型&#xff0c;但是在C中&#xff0c;我们可以模版实现各种类型参数的通用函数。 1. 泛型编程 我们通过函数重载实现多种类型的同一作用的函数。如交换函数&#xff1a; void Swap(int& left, int& right) …

力扣 167.两数之和||—输入为有序数组

文章目录 题目介绍解法 题目介绍 解法 利用相向双指针&#xff0c;初始时l在最用左边&#xff0c;r在最右边 1.numbers[l] numbers[r] < target 则 l 2.numbers[l] numbers[r] < target 则 r 3.numbers[l] numbers[r] target 说明找到了答案 class Solution {publi…

linux 操作系统下dhcrelay命令介绍和案例应用

linux 操作系统下dhcrelay命令介绍和案例应用 dhcrelay是一个用于DHCP&#xff08;动态主机配置协议&#xff09;中继的命令&#xff0c;主要功能是在没有本地DHCP服务器的子网中转发DHCP请求。这使得不同子网的DHCP客户端能够与位于其他子网中的DHCP服务器进行通信。 dhcrela…

基于YOLO算法的网球运动实时分析-击球速度测量-击球次数(附源码)

这个项目通过分析视频中的网球运动员来测量他们的速度、击球速度以及击球次数。该项目使用YOLO&#xff08;You Only Look Once&#xff09;算法来检测球员和网球&#xff0c;并利用卷积神经网络&#xff08;CNNs&#xff09;来提取球场的关键点。此实战项目非常适合提升您的机…

面向对象开发流程例题

答案;C 知识点 面向对象分析 认定对象 组织对象 对象间的相互作用 基于对象的操作 面向对象设计 识别类及对象 定义属性 定义服务 识别关系 识别包 面向对象程序设计 程序设计范型 选择一种OOPL 面向对象测试 算法层 类层 模板层 系统层