建议43: 避免对象的浅拷贝

我们知道一个类实现了Cloneable接口就表示它具备了被拷贝的能力,如果再覆写clone()方法就会完全具备拷贝能力。拷贝是在内存中进行的,所以在性能方面比直接通过new生成对象要快很多,特别是在大对象的生成上,这会使性能的提升非常显著。但是对象拷贝也有一个比较容易忽略的问题:浅拷贝(Shadow Clone,也叫做影子拷贝)存在对象属性拷贝不彻底的问题。我们来看这样一段代码:

1 public classClient {2 public static voidmain(String[] args) {3 //定义父亲

4 Person f = new Person("父亲");5 //定义大儿子

6 Person s1 = new Person("大儿子",f);7 //小儿子的信息是通过大儿子拷贝过来的

8 Person s2 =s1.clone();9 s2.setName("小儿子");10 System.out.println(s1.getName() +" 的父亲是 " +s1.getFather().getName());11 System.out.println(s2.getName() +" 的父亲是 " +s2.getFather().getName());12 }13 }14

15 class Person implementsCloneable{16 //姓名

17 privateString name;18 //父亲

19 privatePerson father;20

21 publicPerson(String _name){22 name =_name;23 }24 publicPerson(String _name,Person _parent){25 name =_name;26 father =_parent;27 }28 /*name和parent的getter/setter方法省略*/

29

30 //拷贝的实现

31 @Override32 publicPerson clone(){33 Person p = null;34 try{35 p = (Person) super.clone();36 } catch(CloneNotSupportedException e) {37 e.printStackTrace();38 }39 returnp;40 }41 publicString getName() {42 returnname;43 }44 public voidsetName(String name) {45 this.name =name;46 }47 publicPerson getFather() {48 returnfather;49 }50 public voidsetFather(Person father) {51 this.father =father;52 }53 }

程序中,我们描述了这样一个场景:一个父亲,有两个儿子,大小儿子同根同种,所以小儿子对象就通过拷贝大儿子对象来生成,运行输出的结果如下:

大儿子 的父亲是 父亲

小儿子 的父亲是 父亲

这很正确,没有问题。突然有一天,父亲心血来潮想让大儿子去认个干爹,也就是大儿子的父亲名称需要重新设置一下,代码如下:

1 public static voidmain(String[] args) {2 //定义父亲

3 Person f = new Person("父亲");4 //定义大儿子

5 Person s1 = new Person("大儿子",f);6 //小儿子的信息是通过大儿子拷贝过来的

7 Person s2 =s1.clone();8 s2.setName("小儿子");9 //认干爹

10 s1.getFather().setName("干爹");11 System.out.println(s1.getName() +" 的父亲是 " +s1.getFather().getName());12 System.out.println(s2.getName() +" 的父亲是 " +s2.getFather().getName());13 }

上面仅仅修改了加粗字体部分,大儿子重新设置了父亲名称,我们期望的输出是:将大儿子父亲的名称修改为干爹,小儿子的父亲名称保持不变。下面来检查一下结果是否如此:

大儿子 的父亲是 干爹

小儿子 的父亲是 干爹

怎么回事,小儿子的父亲也成了“干爹”?两个儿子都没有,岂不是要气死“父亲”了!出现这个问题的原因就在于clone方法,我们知道所有类都继承自Object,Object提供了一个对象拷贝的默认方法,即上面代码中的super.clone方法,但是该方法是有缺陷的,它提供的是一种浅拷贝方式,也就是说它并不会把对象的所有属性全部拷贝一份,而是有选择性的拷贝,它的拷贝规则如下:

(1)基本类型

如果变量是基本类型,则拷贝其值,比如int、float等。

(2)对象

如果变量是一个实例对象,则拷贝地址引用,也就是说此时新拷贝出的对象与原有对象共享该实例变量,不受访问权限的限制。这在Java中是很疯狂的,因为它突破了访问权限的定义:一个private修饰的变量,竟然可以被两个不同的实例对象访问,这让Java的访问权限体系情何以堪!

(3)String字符串

这个比较特殊,拷贝的也是一个地址,是个引用,但是在修改时,它会从字符串池(String Pool)中重新生成新的字符串,原有的字符串对象保持不变,在此处我们可以认为String是一个基本类型。(有关字符串的知识详见第4章。)

明白了这三个规则,上面的例子就很清晰了,小儿子对象是通过拷贝大儿子产生的,其父亲都是同一个人,也就是同一个对象,大儿子修改了父亲名称,小儿子也就跟着修改了—于是,父亲的两个儿子都没了!其实要更正也很简单,clone方法的代码如下:

publicPerson clone(){

Person p= null;try{

p= (Person) super.clone();

p.setFather(newPerson(p.getFather().getName()));

}catch(CloneNotSupportedException e) {

e.printStackTrace();

}returnp;

}

然后再运行,小儿子的父亲就不会是“干爹”了。如此就实现了对象的深拷贝(Deep Clone),保证拷贝出来的对象自成一体,不受“母体”的影响,和new生成的对象没有任何区别。

注意 浅拷贝只是Java提供的一种简单拷贝机制,不便于直接使用。

java 防止拷贝_[改善Java代码]避免对象的浅拷贝相关推荐

  1. java 代码解析工具_改善 Java 代码质量的工具与方法

    原标题:改善 Java 代码质量的工具与方法 我们可能见过上面的有关代码质量的图片,究竟如何衡量一段代码好坏? 代码质量是什么?为什么它很重要? 作家通过他的著作来讲述了一个清晰的.令人信服的故事.他 ...

  2. nio java 内核拷贝_大文件拷贝,试试NIO的内存映射

    最近项目里有个需求需要实现文件拷贝,在java中文件拷贝流的读写,很容易就想到IO中的InputStream和OutputStream之类的,但是上网查了一下文件拷贝也是有很多种方法的,除了IO,还有 ...

  3. java ee 的使用方法_改善Java EE生产支持技能的8种方法

    java ee 的使用方法 参与Java EE生产支持的每个人都知道这项工作可能很困难. 7/24寻呼机支持,多个事件和错误修复(要定期处理),来自客户和管理团队的压力,要求它们尽快解决生产问题并防止 ...

  4. java list拷贝_深入了解浅拷贝与深拷贝

    在学习深拷贝和浅拷贝之前,咱们先来一个例子: import java.util.ArrayList;public class MyBaby implements Cloneable {/*** 私有变 ...

  5. java web源代码_检测Java Web应用程序而无需修改其源代码

    java web源代码 与其他系统进行交互时,大多数Java Web应用程序都使用标准Java接口. 诸如Web页面或REST服务器之类的基于HTTP的服务是使用接口javax.servlet.Ser ...

  6. java 注解应用技巧_改善Java应用程序性能的快速技巧

    java 注解应用技巧 曾经遇到过性能问题吗? 我也是. 如果我的经理再喊一次" faaaaster",我一生都会有听力障碍. 顺便说一句,我能听到所有噪音中的德语发音吗? ;-) ...

  7. java main函数_一行JAVA代码如何运行起来?

    在程序员的世界中,你总会听到一句"PHP是世界上最好的语言"的调侃.然而在你进入软件程序开发之后,你会发现即使开发语言千千万,最盛行的还是JAVA.从淘宝的技术变迁中我们可以见一些 ...

  8. java 内存管理_高性能Java代码之内存管理

    本文通过几个方面,来介绍Java代码的内存管理. 有的代码,GC根本就回收不了,直接系统挂掉.GC是一段程序,不是智能,他只回收他认为的垃圾,而不是回收你认为的垃圾. GC垃圾回收: Grabage ...

  9. java检测工具_常用Java代码质量检测评估工具

    常用Java代码质量检测评估工具 1. PMD from http://pmd.sourceforge.net/ PMD能够扫描Java 源代码,查找类似以下的潜在问题: 可能的bug--try/ca ...

最新文章

  1. python3--装饰器
  2. SSD行业要变天了!因为这种闪存芯片要来
  3. 全国80几所重点大学ftp资源库(经常逛逛可能有惊喜哦)很难收集的,知道其他的友友可以留言完善...
  4. live555推流rtsp_Hi3518 RTSP推流
  5. C++ STL string与算法
  6. 这样写的,一定是辣鸡代码!
  7. 【AI视野·今日Robot 机器人论文速览 第十九期】Mon, 5 Jul 2021
  8. lamp软件包安装(rpm)
  9. 《精通Spring4.X企业应用开发实战》读后感第四章(Java反射)
  10. HTML 标签的 target 属性
  11. 用python统计字母个数_如何用python统计字符串中字母个数?
  12. 页面跳转的两种方式(转发和重定向)区别详解:
  13. Google Jib 容器化构建工具
  14. 世界上最伟大的推销员
  15. Oracle查询某一天数据的SQL语句的几种写法
  16. QT项目之键盘控制光标移动
  17. Somatic selection distinguishes oncogenes and tumor suppressor genes
  18. 计算机网安全模式,电脑安全模式有什么用?
  19. Linux上github提示Permission denied (publickey),如何才能解决?
  20. Java工程师面试题,最全的java手机游戏免费下载基地

热门文章

  1. BCrypt加密怎么存入数据库_第6天 密码加密与微服务鉴权JWT(下)
  2. 离散图 java,Java实现离散Arnold变换(图像处理)
  3. 影驰名人堂送的机器人_玩转GTX 1080Ti名人堂显示屏 影驰全新魔盘使用教程
  4. php新订单提醒代码,PHP怎么实现新订单提醒功能
  5. linux 基于qt assistant制作软件帮助文档,基于Qt Assistant的软件帮助系统
  6. 微型计算机原理综合实验,微机原理综合实验指导书
  7. java list 转 map_高并发下的Java数据结构(List、Set、Map、Queue)
  8. CentOS 8 利用yum源安装nginx
  9. RedHat7.0启动后黑屏
  10. Linux 利用yum源安装php7.0+nginx