Java中的直接赋值、浅拷贝和深拷贝

2023-11-05

将一个对象的引用复制给另外一个对象,一共有三种方式。第一种方式是直接赋值,第二种方式是浅拷贝,第三种是深拷贝。所以大家知道了哈,这三种概念实际上都是为了拷贝对象啊。

直接赋值

好,下面我们先看第一种方式,直接赋值。在Java中,A a1 = a2,我们需要理解的是这实际上复制的是引用,也就是说a1和a2指向的是同一个对象。因此,当a1变化的时候,a2里面的成员变量也会跟着变化。各位,请看下面的代码吧!

package interfaces.nesting;  
  
/* 建立类 */  
class Resume {  
    private String name;  //姓名  
    private String sex;   //性别  
    private int age;      //年龄  
    private String experience; //工作经历  
      
    public Resume(String name, String sex, int age) {  
        this.name = name;  
        this.sex = sex;  
        this.age = age;  
    }  
      
    public void setAge(int age) {  
        this.age = age;  
    }  
    public int getAge() {  
        return age;  
    }  
      
    public void setExperience(String experience) {  
        this.experience = experience;  
    }  
    public String getExperience() {  
        return experience;  
    }  
      
    public void displayResume() {  
        System.out.println("姓名:"+name+" 性别:"+sex+" 年龄:"+age);  
        System.out.println("工作经历:"+experience);  
    }  
}  
  
public class MainClass {  
    public static void main(String[] args) {  
        Resume zhangsan = new Resume("zhangsan","男",24);  
        zhangsan.setExperience("2009-2013就读于家里蹲大学,精通JAVA,C,C++,C#等代码复制");  
        zhangsan.displayResume();  
        Resume zhangsan1 = zhangsan;  
        zhangsan1.setExperience("2009-2013就读于家里蹲大学,精通JAVA,C,C++,C#等");  
        zhangsan.displayResume();  
        zhangsan1.displayResume();  
    }  
}  

程序运行结果:

姓名:zhangsan 性别:男 年龄:24  
工作经历:2009-2013就读于家里蹲大学,精通JAVA,C,C++,C#等代码复制  
姓名:zhangsan 性别:男 年龄:24  
工作经历:2009-2013就读于家里蹲大学,精通JAVA,C,C++,C#等  
姓名:zhangsan 性别:男 年龄:24  
工作经历:2009-2013就读于家里蹲大学,精通JAVA,C,C++,C#等  

在本程序中,生成了一份zhangsan的简历。之后又复制了一份简历zhangsan1,可见zhangsan1中工作经历发生变化时,zhangsan的工作经历也发生了变化。

浅拷贝

上面直接赋值的结果,有时候可能并不是我们所想要的。就像我们投简历的时候,可能会根据应聘公司的类型做出相应的调整,如果是投技术类的工作可能会偏技术一点;如果是投国企啊什么之类的,社会经历学生工作什么的可能也是很重要的一部分。所以我们不需要当我们修改一份简历的时候,所有的简历都变调。不然到时候投技术类的公司又得改回来。说了这么多,我们也就是希望,把a1赋值给a2之后,a1和a2能保持独立,不要互相影响。

实现上面想法之一的方法就是Object的Clone()函数了。在这里,我们需要了解clone()主要做了些什么,创建一个新对象,然后将当前对象的非静态字段复制到该新对象,如果字段是值类型的,那么对该字段执行复制;如果该字段是引用类型的话,则复制引用但不复制引用的对象。因此,原始对象及其副本引用同一个对象。
好,我们先看这一段话的前一部分,如果字段是值类型,则直接复制。如下面程序所示

package interfaces.nesting;  
  
/* 建立类,实现Clone方法  */  
class Resume  implements Cloneable{  
    private String name;  //姓名  
    private String sex;   //性别  
    private int age;      //年龄  
    private String experience; //工作经历  
      
    public Resume(String name, String sex, int age) {  
        this.name = name;  
        this.sex = sex;  
        this.age = age;  
    }  
      
    public void setAge(int age) {  
        this.age = age;  
    }  
    public int getAge() {  
        return age;  
    }  
      
    public void setExperience(String experience) {  
        this.experience = experience;  
    }  
    public String getExperience() {  
        return experience;  
    }  
      
    public void displayResume() {  
        System.out.println("姓名:"+name+" 性别:"+sex+" 年龄:"+age);  
        System.out.println("工作经历:"+experience);  
    }  
      
    public Object clone() {  
        try {  
            return (Resume)super.clone();  
        } catch (Exception e) {  
            e.printStackTrace();  
            return null;  
        }  
    }  
}  
  
public class MainClass {  
    public static void main(String[] args) {  
        Resume zhangsan = new Resume("zhangsan","男",24);  
        zhangsan.setExperience("2009-2013就读于家里蹲大学,精通JAVA,C,C++,C#等代码拷贝和粘贴");  
        zhangsan.displayResume();  
        Resume zhangsan1 = (Resume)zhangsan.clone();  
        zhangsan1.setAge(23);  
        zhangsan1.displayResume();  
        Resume zhangsan2 = (Resume)zhangsan.clone();  
        zhangsan2.setExperience("2009-2013就读于家里蹲大学,精通JAVA,C,C++,C#等代码");  
        zhangsan2.displayResume();  
        zhangsan.displayResume();  
    }  
}  

程序运行结果

姓名:zhangsan 性别:男 年龄:24  
工作经历:2009-2013就读于家里蹲大学,精通JAVA,C,C++,C#等代码拷贝和粘贴  
姓名:zhangsan 性别:男 年龄:23  
工作经历:2009-2013就读于家里蹲大学,精通JAVA,C,C++,C#等代码拷贝和粘贴  
姓名:zhangsan 性别:男 年龄:24  
工作经历:2009-2013就读于家里蹲大学,精通JAVA,C,C++,C#等代码  
姓名:zhangsan 性别:男 年龄:24  
工作经历:2009-2013就读于家里蹲大学,精通JAVA,C,C++,C#等代码拷贝和粘贴  

由程序的运行结果可以看出,我们实现了a1和a2引用的独立。

但是什么叫“如果该字段是引用类型的话,则复制引用但不复制引用的对象。因此,原始对象及其副本引用同一个对象。”,到底什么意思?不用着急,我们接下来看下面一段程序:

package interfaces.nesting;  
  
class Experience {  
      
    private String educationBackground;  
    private String skills;  
      
    public void setExperience(String educationBackground, String skills) {  
        // TODO Auto-generated constructor stub  
        this.educationBackground = educationBackground;  
        this.skills = skills;  
    }  
    public String toString() {  
        return educationBackground + skills;  
    }  
}  
  
/* 建立类,实现Clone方法  */  
class Resume  implements Cloneable{  
    private String name;  //姓名  
    private String sex;   //性别  
    private int age;      //年龄  
    private Experience experience; //工作经历  
      
    public Resume(String name, String sex, int age) {  
        this.name = name;  
        this.sex = sex;  
        this.age = age;  
        this.experience = new Experience();  
    }  
      
    public void setAge(int age) {  
        this.age = age;  
    }  
    public int getAge() {  
        return age;  
    }  
      
    public Experience getExperience() {  
        return experience;  
    }  
      
    public void setExperience(String educationBackground, String skills) {  
        experience.setExperience(educationBackground, skills);  
    }  
      
    public void displayResume() {  
        System.out.println("姓名:"+name+" 性别:"+sex+" 年龄:"+age);  
        System.out.println("工作经历:"+experience.toString());  
    }  
      
    public Object clone() {  
        try {  
            return (Resume)super.clone();  
        } catch (Exception e) {  
            e.printStackTrace();  
            return null;  
        }  
    }  
}  
  
public class MainClass {  
    public static void main(String[] args) {  
        Resume zhangsan = new Resume("zhangsan","男",24);  
        zhangsan.setExperience("2009-2013就读于家里蹲大学","精通JAVA,C,C++,C#等代码拷贝和粘贴");  
        zhangsan.displayResume();  
  
        Resume zhangsan2 = (Resume)zhangsan.clone();  
        zhangsan2.setExperience("2009-2013就读于家里蹲大学","精通JAVA,C,C++,C#等");  
        zhangsan2.displayResume();  
        zhangsan.displayResume();  
        zhangsan2.displayResume();  
    }  
}  

程序运行结果:

姓名:zhangsan 性别:男 年龄:24  
工作经历:2009-2013就读于家里蹲大学精通JAVA,C,C++,C#等代码拷贝和粘贴  
姓名:zhangsan 性别:男 年龄:24  
工作经历:2009-2013就读于家里蹲大学精通JAVA,C,C++,C#等  
姓名:zhangsan 性别:男 年龄:24  
工作经历:2009-2013就读于家里蹲大学精通JAVA,C,C++,C#等  
姓名:zhangsan 性别:男 年龄:24  
工作经历:2009-2013就读于家里蹲大学精通JAVA,C,C++,C#等  

我们看一下上面两段程序差异在哪儿,第一段程序的工作经历是作为Resume类的一个普通的成员变量,也就是值属性。而后面一段程序中,工作经历Experience是一个类。结合上面程序的运行结果,我们再来理解“如果该字段是引用类型的话,则复制引用但不复制引用的对象。因此,原始对象及其副本引用同一个对象。”其实也就是说,zhangsan和zhangsan2里面的Experience类指向的是同一个对象嘛!那不管是zhangsan里面的Experience变化,还是zhangsan2里面的Experience变化都会影响另外一个啊。

浅拷贝,大家懂没?Over了啊!

深拷贝

由前面的分析,浅拷贝无法实现含有其他对象引用的本对象的拷贝。那么很显然,深拷贝,就是说创建一个新对象,然后将当前对象的非静态字段复制到该新对象,无论该字段是值类型的还是引用类型,都乖乖的进行复制。
有了这个出发点,其实改起来很好改啊。浅拷贝的死穴就在于原始对象及其副本引用同一个对象,那我们让他们不指向同一个对象不就完了嘛!见代码:

package interfaces.nesting;  
  
class Experience {  
      
    private String educationBackground;  
    private String skills;  
      
    public void setExperience(String educationBackground, String skills) {  
        // TODO Auto-generated constructor stub  
        this.educationBackground = educationBackground;  
        this.skills = skills;  
    }  
    public String toString() {  
        return educationBackground + skills;  
    }  
}  
  
/* 建立类,实现Clone方法  */  
class Resume  implements Cloneable{  
    private String name;  //姓名  
    private String sex;   //性别  
    private int age;      //年龄  
    private Experience experience; //工作经历  
      
    public Resume(String name, String sex, int age) {  
        this.name = name;  
        this.sex = sex;  
        this.age = age;  
        this.experience = new Experience();  
    }  
      
    public void setAge(int age) {  
        this.age = age;  
    }  
    public int getAge() {  
        return age;  
    }  
      
    public Experience getExperience() {  
        return experience;  
    }  
      
    public void setExperience(String educationBackground, String skills) {  
        experience = new Experience();  
        experience.setExperience(educationBackground, skills);  
    }  
      
    public void displayResume() {  
        System.out.println("姓名:"+name+" 性别:"+sex+" 年龄:"+age);  
        System.out.println("工作经历:"+experience.toString());  
    }  
      
    public Object clone() {  
        try {  
            return (Resume)super.clone();  
        } catch (Exception e) {  
            e.printStackTrace();  
            return null;  
        }  
    }  
}  
  
public class MainClass {  
    public static void main(String[] args) {  
        Resume zhangsan = new Resume("zhangsan","男",24);  
        zhangsan.setExperience("2009-2013就读于家里蹲大学","精通JAVA,C,C++,C#等代码拷贝和粘贴");  
        zhangsan.displayResume();  
  
        Resume zhangsan2 = (Resume)zhangsan.clone();  
        zhangsan2.setExperience("2009-2013就读于家里蹲大学","精通JAVA,C,C++,C#等");  
        zhangsan2.displayResume();  
        zhangsan.displayResume();  
        zhangsan2.displayResume();  
    }  
}  

程序运行结果:

姓名:zhangsan 性别:男 年龄:24  
工作经历:2009-2013就读于家里蹲大学精通JAVA,C,C++,C#等代码拷贝和粘贴  
姓名:zhangsan 性别:男 年龄:24  
工作经历:2009-2013就读于家里蹲大学精通JAVA,C,C++,C#等  
姓名:zhangsan 性别:男 年龄:24  
工作经历:2009-2013就读于家里蹲大学精通JAVA,C,C++,C#等代码拷贝和粘贴  
姓名:zhangsan 性别:男 年龄:24  
工作经历:2009-2013就读于家里蹲大学精通JAVA,C,C++,C#等  
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Java中的直接赋值、浅拷贝和深拷贝 的相关文章

  • 从 java sdk 向对等方发送提案时出现访问被拒绝错误

    我正在尝试使用以下代码查询区块链并收到访问被拒绝错误 我也遇到同样的错误sendTransactionProposal方法也是如此 UserContext adminUserContext RegisterEnrollUser regist
  • 存根方法时出现 InvalidUseOfMatchersException

    我有这个 TestNG 测试方法代码 InjectMocks private FilmeService filmeService new FilmeServiceImpl Mock private FilmeDAO filmeDao Bef
  • 如何将 javax.persistence.Column 定义为 Unsigned TINYINT?

    我正在基于 MySQL 数据库中的现有表创建 Java 持久性实体 Bean 使用 NetBeans IDE 8 0 1 我在这个表中遇到了一个字段 其类型为 无符号 TINYINT 3 我发现可以执行以下操作将列的类型定义为 unsign
  • 如何将画廊意图中的“打开”更改为“完成”?

    我使用以下意图打开画廊来选择多个图像和视频 Intent intent new Intent intent setType image video intent putExtra Intent EXTRA ALLOW MULTIPLE tr
  • Java Runtime.getRuntime().freeMemory() 问题

    我搜索并看到了一些线程 但没有一个能够解决我遇到的具体问题 我正在尝试使用以下方式监视我的内存使用情况Runtime getRuntime freeMemory Runtime getRuntime maxMemory and Runtim
  • 使用 Ant 将非代码资源添加到 jar 文件

    我正在将 java 应用程序打包成 jar 文件 我正在使用 ant 和 eclipse 我实际上需要在 jar 中直接在根文件夹下包含几个单独的非代码文件 xml 和 txt 文件 而不是与代码位于同一位置 我正在尝试使用includes
  • org.hibernate.QueryException:无法解析属性:文件名

    我正在使用休眠Criteria从列中获取值filename在我的桌子上contaque recording log 但是当我得到结果时 它抛出异常 org hibernate QueryException 无法解析属性 文件名 com co
  • 大数据使用什么数据结构

    我有一个包含一百万行的 Excel 工作表 每行有 100 列 每行代表一个具有 100 个属性的类的实例 列值是这些属性的值 哪种数据结构最适合在这里使用来存储数百万个数据实例 Thanks 这实际上取决于您需要如何访问这些数据以及您想要
  • 如何将 Mat (opencv) 转换为 INDArray (DL4J)?

    我希望任何人都可以帮助我解决这个任务 我正在处理一些图像分类并尝试将 OpenCv 3 2 0 和 DL4J 结合起来 我知道DL4J也包含Opencv 但我认为它没什么用 谁能帮我 如何转换成 INDArray 我尝试阅读一些问题here
  • 当客户端关闭连接时,Spring StreamingResponseBody 请求线程未清理

    我在控制器中有一个端点 它返回一个StreamingResponseBody 用于向客户端发送文件 其代码大致如下 RestController RequestMapping value api public class Controlle
  • 什么时候可以在 Java 中使用 Thead.stop() ?

    Thread stop 的 Java 文档听起来好像如果您调用 Thread stop 世界就会终结 已弃用 这种方法本质上是不安全的 停止线程 Thread stop 导致它解锁所有已锁定的监视器 作为未经检查的 ThreadDeath
  • Jackson XML ArrayList 输出具有两个包装器元素

    我在 Jackson 生成的 XML 输出中得到了两个包装器元素 我只想拥有一个 我有一个 Java bean Entity Table name CITIES JacksonXmlRootElement localName City pu
  • 套接字的读写如何同步?

    我们创建一个套接字 在套接字的一侧有一个 服务器 在另一侧有一个 客户端 服务器和客户端都可以向套接字写入和读取 这是我的理解 我不明白以下事情 如果服务器从套接字读取数据 它在套接字中是否只看到客户端写入套接字的内容 我的意思是 如果服务
  • 了解joda时间PeriodFormatter

    我以为我明白了 但显然我不明白 你能帮我通过这些单元测试吗 Test public void second assertEquals 00 00 01 OurDateTimeFormatter format 1000 Test public
  • 读取电子邮件的文本文件转换为 Javamail MimeMessage

    我有一个电子邮件原始来源的文本文件 直接从 gmail 复制 如果您单击 查看原始文件 您就会看到它 我想读入该文件并将其转换为 MimeMessage 如果您好奇为什么 我设置了 JavaMaildir 并且需要用电子邮件填充它的收件箱以
  • java库维护数据库结构

    我的应用程序一直在开发 所以偶尔 当版本升级时 需要创建 更改 删除一些表 修改一些数据等 通常需要执行一些sql代码 是否有一个 Java 库可用于使我的数据库结构保持最新 通过分析类似 db structure version 信息并执
  • 是否可以使用 Java Guava 将函数应用于集合?

    我想使用 Guava 将函数应用于集合 地图等 基本上 我需要调整 a 的行和列的大小Table分别使所有行和列的大小相同 执行如下操作 Table
  • “无法实例化活动”错误

    我的一个 Android 应用程序拥有大约 100 000 个用户 每周大约 10 次 我会通过 Google 的市场工具向我报告以下异常情况 java lang RuntimeException Unable to instantiate
  • 在浏览器刷新中刷新检票面板

    我正在开发一个付费角色系统 一旦用户刷新浏览器 我就需要刷新该页面中可用的统计信息 统计信息应该从数据库中获取并显示 但现在它不能正常工作 因为在页面刷新中 java代码不会被调用 而是使用以前的数据加载缓存的页面 我尝试添加以下代码来修复
  • 在java中使用多个bufferedImage

    我正在 java 小程序中制作游戏 并且正在尝试优化我的代码以减少闪烁 我已经实现了双缓冲 因此我尝试使用另一个 BufferedImage 来存储不改变的游戏背景元素的图片 这是我的代码的相关部分 public class QuizApp

随机推荐

  • 阿里云服务器更换操作系统流程(新版教程)

    阿里云服务器操作系统选择后还可以更改吗 可以的 重装操作系统是免费的 但是需要注意中国大陆地域可以随意更换 中国香港 新加坡等非中国大陆地域的云服务器不支持Windows和Linux操作系统之间变换 阿里云百科来详细说下阿里云服务器重装操作
  • CentOS8、Ubuntu安装mysql5.7,修改密码、配置远程访问、开机自启、创建开发者账户(超级详细)

    下载MYSQL wget http dev mysql com get mysql80 community release el7 3 noarch rpm 安装MYSQL RPM 模块 rpm ivh mysql80 community
  • m3u8文件

    EXTM3U m3u文件头 必须放在第一行 起标示作用 EXT X VERSION 播放列表文件的兼容版本 若不存在此标记 则默认为协议的第一个版本 EXT X MEDIA SEQUENCE 播放列表中的每个媒体 URI 都有一个唯一的整数
  • docker容器内修改配置文件

    一 vim修改 1 进入容器内部 docker exec it id bin bash 2 安装vim 输入命令 apt get update apt get install vim 二 替换配置文件 1 替换配置文件 docker cp
  • 最火前端Web组态软件(可视化)

    前言 随着物联网 大数据等技术高速发展 我们逐步向数字化 可视化的人工智能 AI 时代的方向不断迈进 智能时代是工业 4 0 时代 我国工业领域正努力从 制造 迈向 智造 的新跨越 正文 1 mxgraph 介绍 开源免费 但是需要解决的问
  • CososCreator (Android)-AppLovin MAX 广告聚合平台接入+Firebase统计

    CososCreator 2 4 4 Android Studio 4 2 1 接入SDK有 接max聚合及中介平台 Admob FB applovin pangle mintegral vungle unity 和Firebase 统计
  • kubernetes(k8s)介绍

    记录自己的学习历程 应用部署方式演变 在部署应用程序的方式上 主要经历了三个时代 传统部署 互联网早期 会直接将应用程序部署在物理机上 优点 简单 不需要其它技术的参与 缺点 不能为应用程序定义资源使用边界 很难合理地分配计算资源 而且程序
  • 入门Webpack,看这篇就够了

    写在前面的话 阅读本文之前 先看下面这个webpack的配置文件 如果每一项你都懂 那本文能带给你的收获也许就比较有限 你可以快速浏览或直接跳过 如果你和十天前的我一样 对很多选项存在着疑惑 那花一段时间慢慢阅读本文 你的疑惑一定一个一个都
  • 笔试

    文章目录 前言 41 关键路径与路径优化 何为关键路径 怎么进行路径优化 1 组合逻辑中插入寄存器 插入流水线 2 寄存器平衡 重定时Retiming 3 操作符平衡 加法树 乘法树 4 消除代码优先级 case代替if else 5 逻辑
  • linux通过yum和官网下载jdk安装java步骤

    1 yum方式下载安装 1 查找java相关的列表 yum y list java 或者 yum search jdk 2 安装jdk yum install java 1 8 0 openjdk x86 64 3 完成安装后验证 java
  • FreeRTOS学习(八) 延时函数

    声明及感谢 跟随正点原子资料学习 在此作为学习的记录和总结 环境 keil stm32f103 FreeRTOS延时函数有两个 分别是 vTaskDelay vTaskDelayUntil 1 vTaskDelay 任务相对延时 函数原型
  • elk笔记21--将DSL查询转为kibana短链接

    elk笔记21 将DSL查询转为kibana短链接 1 简介 2 功能实现 2 1 源码 2 2 测试 3 注意事项 4 说明 1 简介 较新版本的 kibana 前端都有一个 Share gt Short URL 的功能 用起来非常方便
  • springBoot项目与Eureka注册中心整合时候关于版本兼容问题

    java lang NoSuchMethodError org springframework boot builder SpringApplicationBuilder Ljava lang Object V 如果在搭建springboo
  • python基础总结:1.7、模块

    python基础总结 1 7 模块 文章目录 python基础总结 1 7 模块 1 更多有关模块的信息 1 1 以脚本的方式执行模块 1 2 模块搜索路径 1 3 编译过的 python文件 2 标准模块 3 dir 函数 4 包 4 1
  • 区分电角度和机械角度

    电角度的区别和联系 以下内容均来自于网络搜索 这里仅用作于自己记录笔记 解释1 机械角度意为空间几何角度 相当于360 电角度是指通过一个完整的周期内电流完成变化 从电磁分布的角度来看 永磁体 或励磁 产生的磁场空间分布呈现周期性变化 一个
  • 《SQL中有关DQL、DML、DDL、DCL的概念与区别》

    SQL Structure Query Language 结构化查询语言是数据库的核心语言 是高级的非过程化编程语言 它功能强大 效率高 简单易学易维护 SQL语言基本上独立于数据库本身 使用的机器 网络 操作系统 基于SQL的DBMS产品
  • 解决使用layui上传文件时提示“请求上传接口出现异常”

    在做网站的过程中 需要用户上传文件 接收文件的接口已经写好 经过测试确定可用 现在需要在网页上提供一个文件上传的按钮 由于原生input type file 组件太丑 考虑使用现成的框架layui 跑到layui官网上查看官方文档进行学习
  • 2023必须掌握的10大Web3技术

    2023必须掌握的10大Web3技术 1 区块链技术 Blockchain Technology 2 智能合约 Smart Contracts 3 物联网 IoT Internet of Things 4 云计算 Cloud Computi
  • git报错fatal: unable to access ‘XXX‘: SSL certificate problem: certificate has expired

    一 问题 在git bash中执行git clone xxx之后 并没有弹出登陆框 而是报以下错误 git报错fatal unable to access XXX SSL certificate problem certificate ha
  • Java中的直接赋值、浅拷贝和深拷贝

    将一个对象的引用复制给另外一个对象 一共有三种方式 第一种方式是直接赋值 第二种方式是浅拷贝 第三种是深拷贝 所以大家知道了哈 这三种概念实际上都是为了拷贝对象啊 直接赋值 好 下面我们先看第一种方式 直接赋值 在Java中 A a1 a2