对象克隆+深浅拷贝

【0】README

0.1) 本文描述+源代码均 转自 core java volume 1, 旨在理解 对象拷贝 的概念 , 特别是对 深拷贝和浅拷贝 的理解;
0.2) 最后,我们还要看一个 clone 的荔枝;


【1】对象克隆相关

1.1)出现的问题: 当copy一个变量时, 原始变量和 copy 变量引用同一个对象, 也就是说, 改变一个变量所引用的对象将会 对另一个变量产生影响;

Employee original = new Employee("tang", 50000);
Employee copy = original;
copy.raiseSalary(10);//oops -- also changed original

这里写图片描述
1.2)解决方法:

  • 1.2.1)如果创建一个对象的新的copy, 它的最初状态与 original一样, 但以后将可以各自改变各自的状态, 那就需要使用clone 方法:
Employee copy = original.clone();
copy.raiseSalary(10); // OK--original unchanged
  • 1.2.2)不过,事情没有那么简单。clone 方法是 Object类的 protected方法, 也就是说, 在用户编写的代码中不能直接调用它, 只有Employee 类才能克隆 Employee 对象;
  • 1.2.3)浅拷贝:如果对象中的所有数据域都属于数值或者基本类型, 只有copy 域是没有问题的。 但是,如果 在对象中包含了子对象的引用, copy 的结果 会使得域 引用同一个子对象, 因此原始对象与 克隆对象共享这部分信息, 这就叫做浅拷贝; 要知道, Object类的clone方法克隆Employee 对象的结果, 可以看到, 默认的克隆操作是 浅拷贝, 它并没有克隆包含在对象中的内部对象;
    这里写图片描述

【2】浅拷贝和深拷贝

2.1)深拷贝:如果原始对象 与 浅拷贝对象共享的子对象是不可变的, 将不会产生任何问题;然而,更常见的 是 子对象是可变的, 因此必须重新定义 clone 方法, 以便实现 克隆子对象的 深拷贝;
2.2)在列举的实例中, hireDay域 属于 Date类, 这就是一个可变的子对象;
2.3)对于每一个类, 都需要做如下判断:

  • 2.3.1)默认的clone方法是否满足要求;
  • 2.3.2)默认的 clone 方法是否能够通过 调用可变子对象的 clone 得到修补;
  • 2.3.3)是否不应该使用 clone;

2.4)实际上, 选项3是默认的, 如果要选择1或2, 类必须:

  • 2.4.1)实现 Cloneable 接口;
  • 2.4.2)使用 public 访问修饰符重新定义 clone方法;

Attention)

  • A1) 在Object类中,clone方法被说明为 protected, 因此无法直接调用 anObject.clone()。但是, 不是所有子类都可以访问 受保护的方法吗?不是每个类都是Object的子类吗?值得庆幸的是, 受保护访问的规则极为微妙。子类只能调用受保护的clone方法克隆它自己。为此, 必须重新定义clone方法, 并将它声明为 public, 这样才能够让 所有方法克隆对象;
  • A2) Cloneable接口的出现与接口的正常使用没有任何关系。尤其是, 它并没有指定clone方法, 这个是从 Object类继承来的。接口在这里只是作为一个标记, 表明类设计者知道要进行克隆处理。如果一个对象需要克隆,而没有实现Cloneable 接口,就会产生一个已检验异常;
  • A3) Cloneable接口是 java提供的几个标记接口之一, 我们知道, 通常使用接口的目的是为了确保类实现某个特定的方法或一组特定的方法, Comparable接口就是这样一个实例。而标记接口没有方法, 使用它的唯一目的是可以用 instanceof 进行类型检查: if(obj instanceof Cloneable) , 但是我们建议不这样使用;

2.5)即使clone 的默认实现(浅拷贝)能够满足需求, 也应该实现 Cloneable接口, 将 clone 重定义为 public, 并调用super.clone();

class Employee implements Cloneable
{public Employee() throws CloneNotSupportedException{return (Employee)super.clone();}
}

2.6)为了实现深拷贝, 必须克隆所有可变的实例域(Key):

class Employee implements Cloneable
{public Employee() throws CloneNotSupportedException{Employee cloned = (Employee)super.clone();cloned.hireDay = (Date)hireDay.clone();}
}

2.7)只要在 clone 中含有没有实现 Cloneable接口的对象,Object类的 clone方法就会抛出一个CLoneNotSupportedException异常。所以,我们需要声明异常:

public Employee clone() throws CloneNotSupportedException
  • 2.7.1)如果将 上面的形式替换为 try-catch呢?
public Employee() 
{try{return (Employee)super.clone();}catch(CloneNotSupportedException e) {return null;}
}

Attention)以上写法比较适用于 final类,否则最好还是保留 throws的形式;
2.8)必须谨慎地实现子类的克隆

  • 2.8.1)如, 一旦 Employee定义了clone方法,那就可以用它来克隆 Manager对象。Employee的克隆方法能够完成这项任务吗? 这将取决于 Manager类中包含哪些域;在 Manager类中有可能存在需要深拷贝的域, 或者包含一些没有实现 Cloneable 接口的域;
  • 2.8.2)没有人能够保证子类实现的clone 一定正确。鉴于这个原因, 应该将 Object类中的clone方法声明为 protected;

2.9)在自定义的类中应该实现 clone方法吗?
如果客户需要深拷贝,那就应该实现它。而且, 克隆的应用并不像人们想象的那样普遍, 在标准类库中, 只有不到 5%的类实现了 clone;
Annotation)所有的数组类型均包含clone方法, 这个方法被设为 public, 而不是 protected。 可以利用 这个 方法创建一个包含所有数据元素拷贝的一个新数组,如:

int[] luckyNumbers = {2, 3, 5, 7, 11, 13];
int[] cloned = luckyNumbers.clone();
cloned[5] = 12; // doesn't change luckyNumbers[5]

【3】看个荔枝:

package com.corejava.chapter6_2;import java.util.Date;
import java.util.GregorianCalendar;public class Employee implements Cloneable
{private String name;private double salary;private Date hireDay;public Employee(String n, double s){name = n;salary = s;hireDay = new Date();}public Employee clone() throws CloneNotSupportedException{// call Object.clone()Employee cloned = (Employee) super.clone();// clone mutable fieldscloned.hireDay = (Date) hireDay.clone();return cloned;}/*** Set the hire day to a given date. * @param year the year of the hire day* @param month the month of the hire day* @param day the day of the hire day*/public void setHireDay(int year, int month, int day){Date newHireDay = new GregorianCalendar(year, month - 1, day).getTime();// Example of instance field mutationhireDay.setTime(newHireDay.getTime());}public void raiseSalary(double byPercent){double raise = salary * byPercent / 100;salary += raise;}public String toString(){return "Employee[name=" + name + ",salary=" + salary + ",hireDay=" + hireDay + "]";}
}package com.corejava.chapter6_2;/*** This program demonstrates cloning.* @version 1.10 2002-07-01* @author Cay Horstmann*/
public class CloneTest
{public static void main(String[] args){try{Employee original = new Employee("John Q. Public", 50000);original.setHireDay(2000, 1, 1);Employee copy = original.clone();copy.raiseSalary(10);copy.setHireDay(2002, 12, 31);System.out.println("original=" + original);System.out.println("copy=" + copy);}catch (CloneNotSupportedException e){e.printStackTrace();}}
}

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

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

相关文章

read cache_通过READ-BEHIND CACHE控制您的慢速生产者

read cache在我们的互联世界中,我们经常使用我们不拥有或无权改善的API中的数据。 如果一切顺利,他们的表现就会很好,每个人都会高兴。 但是太多次,我们不得不使用延迟小于最佳延迟的 API。 当然,答案是缓存该数据 。…

azkaban mysql参数_azkaban参数详解

参数传递是调度字体工作流运行时非常重要的一部分,工作流的执行,单个作业的执行,多个工作流之间的依赖执行,历史任务重算,都涉及到参数传递和同步。1 参数类型综述azkaban的工作流中的参数可以分为如下几个类型&#x…

接口与回调

【0】README 0.1) 本文描述源代码均 转自 core java volume 1, 旨在理解 接口与回调 概念 ; 【1】接口与回调相关 1.1)回调定义: 回调是一种常见的程序设计模式, 在这种模式中, 可以指出某个…

spring react_使用Spring Cloud Gateway保护React式微服务

spring react朋友不允许朋友写用户身份验证。 厌倦了管理自己的用户? 立即尝试Okta的API和Java SDK。 数分钟之内即可在任何应用程序中对用户进行身份验证,管理和保护。 所以你想完全React,是吗? 大! React式编程是使…

mysql断电同步不起作用_mysql主从同步因断电产生的不能同步问题

偶尔因为断电导致mysql slave 出现复制错误“Could not parse relay log event entry”Could not parse relay log event entry. The possible reasons are: the masters binary log is corrupted (you can check this by running mysqlbinlog on the binary log), the slaves …

图论——Dijkstra+prim算法涉及到的优先队列(二叉堆)

【0】README 0.1)为什么有这篇文章?因为 Dijkstra算法的优先队列实现 涉及到了一种新的数据结构,即优先队列(二叉堆)的操作需要更改以适应这种新的数据结构,我们暂且吧它定义为Distance, 而不是…

cucumber测试_如何在Cucumber中进行后端测试

cucumber测试Cucumber是一种规范语言的执行框架。 它并不是要成为测试语言,而是用于创建测试自动化。 Cucumber最适合出现一些实际参与者互动并取得某种成果的情况。 当可以从用户的角度编写它时,它特别有用。 Given Sarah is a premium club member W…

linux mysql删除密码忘记了_linux下忘记mysql密码的几种找回方法(推荐)

今天我们主要是讲一下关于linux忘记mysql密码处理方法,下面提供了5种linux忘记mysql密码找回方法哦。方法一(先进入root权限):# /etc/init.d/mysql stop# mysqld_safe --usermysql --skip-grant-tables --skip-networking &# mysql -u rootmysql>…

Dijkstra 算法——计算有权最短路径(边有权值)

【0】README 0.1) 本文总结于 数据结构与算法分析, 源代码均为原创, 旨在理解 Dijkstra 的思想并用源代码加以实现; 0.2)最短路径算法的基础知识,参见 http://blog.csdn.net/pacosonswjtu/article/detail…

spring使用自定义注解_用Spring组成自定义注释

spring使用自定义注解Java批注在2004年随Java 5一起引入,是一种将元数据添加到Java源代码中的方法。 如今,许多主要框架(如Spring或Hibernate)都严重依赖注释。 在本文中,我们将介绍一个非常有用的Spring功能&#xf…

打印结果和调试结果不一样(C语言)

【0】README 0.1)本文旨在阐述 个人的debug经历,遇到的各种debug 奇葩问题, 说是奇葩,其实也是自己 不小心或者说是编程习惯不好; 【1】debug和running的运行结果不一致(乍眼一看,你肯定醉了&a…

mysql add default_MySQL中create table DEFAULT 用法

CREATE [TEMPORARY] TABLE [IF NOT EXISTS] tbl_name [(create_definition,...)][table_options] [select_statement]TEMPORARY:该关键字表示用create table新建的表为临时表,此表在当前会话结束后将自动消失。临时表主要被应用于存储过程中,…

jakarta ee_Jakarta EE贡献–入门

jakarta ee您是否有兴趣帮助Jakarta EE向前发展? 我也是。我想提供一些详细信息,以帮助有兴趣入门的人。 第1步: 开始捐款的第一步是签署Eclipse Foundation Committer and Contributor Agreement(ECA): …

最小生成树基础

【0】README 0.1) 本文总结于 数据结构与算法分析, 源代码均为原创, 旨在 review 最小生成树的基础知识; 0.2)了解本文的内容是 分析 Prim算法(普利姆算法)和 Kruskal算法(克鲁斯卡…

mysql dump gtid_mysqldump命令详解 Part 3- 备份全库

前面说了MySQL Linux平台和Windows平台的安装下面开始是MySQL的一些学习笔记前面我们说了如果构造数据这节开始说MySQL 的备份环境为MySQL 5.7.25在解释命令之前我们先弄清楚数据库中有哪些对象上一节我们建立了数据库并建立相关的对象数据库表存储过程函数触发器事件这节讲一些…

apache lucene_Apache Lucene中的并发查询执行

apache luceneApache Lucene是一个出色的并发纯Java搜索引擎,如果您愿意,它可以轻松地使服务器上的可用CPU或IO资源饱和。 “典型” Lucene应用程序的并发模型在搜索时每个查询一个线程,但是您是否知道Lucene也可以使用多个线程同时执行一个查…

最小生成树——Prim(普利姆)算法

【0】README 0.1) 本文总结于 数据结构与算法分析, 源代码均为原创, 旨在 理解Prim算法的idea 并用 源代码加以实现; 0.2)最小生成树的基础知识,参见 http://blog.csdn.net/pacosonswjtu/article/details…

mysql grant usage on_grant 权限 on 数据库对象 to 用户

grant 权限 on 数据库对象 to 用户一、grant 普通数据用户,查询、插入、更新、删除 数据库中所有表数据的权利。grant select on testdb.* to common_user’%’grant insert on testdb.* to common_user’%’grant update on testdb.* to common_user’%’grant del…

openjdk8 项目结构_OpenJDK织机和结构化并发

openjdk8 项目结构Project Loom是Hotspot Group赞助的项目之一,旨在向JAVA世界提供高吞吐量和轻量级的并发模型。 在撰写本文时,Loom项目仍在积极开发中,其API可能会更改。 为什么要织机? 每个新项目可能会出现的第一个问题是为什…

mysql连库串_数据库连接串整理 - osc_ac5z111b的个人空间 - OSCHINA - 中文开源技术交流社区...

常用JDBC驱动与连接字符串MySQLdriver:com.mysql.jdbc.Driverurl:jdbc:mysql://localhost:3306/mydbMySQL url格式:jdbc:mysql://[host:port]/[database][?参数名1][参数值1][&参数名2][参数值2]…参数名称参数说明缺省值最低版本要求us…