java 防止拷贝_[改善Java代码]避免对象的浅拷贝
建议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代码]避免对象的浅拷贝相关推荐
- java 代码解析工具_改善 Java 代码质量的工具与方法
原标题:改善 Java 代码质量的工具与方法 我们可能见过上面的有关代码质量的图片,究竟如何衡量一段代码好坏? 代码质量是什么?为什么它很重要? 作家通过他的著作来讲述了一个清晰的.令人信服的故事.他 ...
- nio java 内核拷贝_大文件拷贝,试试NIO的内存映射
最近项目里有个需求需要实现文件拷贝,在java中文件拷贝流的读写,很容易就想到IO中的InputStream和OutputStream之类的,但是上网查了一下文件拷贝也是有很多种方法的,除了IO,还有 ...
- java ee 的使用方法_改善Java EE生产支持技能的8种方法
java ee 的使用方法 参与Java EE生产支持的每个人都知道这项工作可能很困难. 7/24寻呼机支持,多个事件和错误修复(要定期处理),来自客户和管理团队的压力,要求它们尽快解决生产问题并防止 ...
- java list拷贝_深入了解浅拷贝与深拷贝
在学习深拷贝和浅拷贝之前,咱们先来一个例子: import java.util.ArrayList;public class MyBaby implements Cloneable {/*** 私有变 ...
- java web源代码_检测Java Web应用程序而无需修改其源代码
java web源代码 与其他系统进行交互时,大多数Java Web应用程序都使用标准Java接口. 诸如Web页面或REST服务器之类的基于HTTP的服务是使用接口javax.servlet.Ser ...
- java 注解应用技巧_改善Java应用程序性能的快速技巧
java 注解应用技巧 曾经遇到过性能问题吗? 我也是. 如果我的经理再喊一次" faaaaster",我一生都会有听力障碍. 顺便说一句,我能听到所有噪音中的德语发音吗? ;-) ...
- java main函数_一行JAVA代码如何运行起来?
在程序员的世界中,你总会听到一句"PHP是世界上最好的语言"的调侃.然而在你进入软件程序开发之后,你会发现即使开发语言千千万,最盛行的还是JAVA.从淘宝的技术变迁中我们可以见一些 ...
- java 内存管理_高性能Java代码之内存管理
本文通过几个方面,来介绍Java代码的内存管理. 有的代码,GC根本就回收不了,直接系统挂掉.GC是一段程序,不是智能,他只回收他认为的垃圾,而不是回收你认为的垃圾. GC垃圾回收: Grabage ...
- java检测工具_常用Java代码质量检测评估工具
常用Java代码质量检测评估工具 1. PMD from http://pmd.sourceforge.net/ PMD能够扫描Java 源代码,查找类似以下的潜在问题: 可能的bug--try/ca ...
最新文章
- python3--装饰器
- SSD行业要变天了!因为这种闪存芯片要来
- 全国80几所重点大学ftp资源库(经常逛逛可能有惊喜哦)很难收集的,知道其他的友友可以留言完善...
- live555推流rtsp_Hi3518 RTSP推流
- C++ STL string与算法
- 这样写的,一定是辣鸡代码!
- 【AI视野·今日Robot 机器人论文速览 第十九期】Mon, 5 Jul 2021
- lamp软件包安装(rpm)
- 《精通Spring4.X企业应用开发实战》读后感第四章(Java反射)
- HTML 标签的 target 属性
- 用python统计字母个数_如何用python统计字符串中字母个数?
- 页面跳转的两种方式(转发和重定向)区别详解:
- Google Jib 容器化构建工具
- 世界上最伟大的推销员
- Oracle查询某一天数据的SQL语句的几种写法
- QT项目之键盘控制光标移动
- Somatic selection distinguishes oncogenes and tumor suppressor genes
- 计算机网安全模式,电脑安全模式有什么用?
- Linux上github提示Permission denied (publickey),如何才能解决?
- Java工程师面试题,最全的java手机游戏免费下载基地
热门文章
- BCrypt加密怎么存入数据库_第6天 密码加密与微服务鉴权JWT(下)
- 离散图 java,Java实现离散Arnold变换(图像处理)
- 影驰名人堂送的机器人_玩转GTX 1080Ti名人堂显示屏 影驰全新魔盘使用教程
- php新订单提醒代码,PHP怎么实现新订单提醒功能
- linux 基于qt assistant制作软件帮助文档,基于Qt Assistant的软件帮助系统
- 微型计算机原理综合实验,微机原理综合实验指导书
- java list 转 map_高并发下的Java数据结构(List、Set、Map、Queue)
- CentOS 8 利用yum源安装nginx
- RedHat7.0启动后黑屏
- Linux 利用yum源安装php7.0+nginx