常范的对象克隆错误

【第32题】JAVA高级技术-对象克隆1(假克隆) 对象克隆系列-假克隆 阅读详情

一、常见错误1# :多次拷贝字符串

测试所不能发现的一个错误是生成不可变(immutable)对象的多份拷贝。不可变对象是不可改变的,因此不需要拷贝它。最常用的不可变对象是String。

如果你必须改变一个String对象的内容,你应该使用StringBuffer。下面的代码会正常工作:

  1. String s = new String ("Text here"); 

但是,这段代码性能差,而且没有必要这么复杂。你还可以用以下的方式来重写上面的代码:

  1. String temp = "Text here";   
  2. String s = new String (temp); 

但是这段代码包含额外的String,并非完全必要。更好的代码为:

  1. String s = "Text here"; 

二、常见错误2#: 没有克隆(clone)返回的对象

封装(encapsulation)是面向对象编程的重要概念。不幸的是,Java为不小心打破封装提供了方便——Java允许返回私有数据的引用(reference)。下面的代码揭示了这一点:

  1.  
  2. import java.awt.Dimension;   
  3. /***Example class.The x and y values should never*be negative.*/   
  4. public class Example{   
  5.   private Dimension d = new Dimension (0, 0);   
  6.   public Example (){ }  
  7.   /*** Set height and width. Both height and width must be nonnegative * 
  8. or an exception is thrown.*/   
  9.   public synchronized void setValues (int height,int width) throws IllegalArgumentException{   
  10. if (height < 0 || width < 0)   
  11.   throw new IllegalArgumentException();   
  12.   d.height = height;   
  13. d.width = width;   
  14.   }  
  15.   public synchronized Dimension getValues(){   
  16. // Ooops! Breaks encapsulation   
  17. return d;   
  18.   }   
  19. } 

Example类保证了它所存储的height和width值永远非负数,试图使用setValues()方法来设置负值会触发异常。不幸的是,由于getValues()返回d的引用,而不是d的拷贝,你可以编写如下的破坏性代码:

  1. Example ex = new Example();   
  2. Dimension d = ex.getValues();   
  3. d.height = -5;   
  4. d.width = -10; 

现在,Example对象拥有负值了!如果getValues() 的调用者永远也不设置返回的Dimension对象的width 和height值,那么仅凭测试是不可能检测到这类的错误。

不幸的是,随着时间的推移,客户代码可能会改变返回的Dimension对象的值,这个时候,追寻错误的根源是件枯燥且费时的事情,尤其是在多线程环境中。

更好的方式是让getValues()返回拷贝:

  1. public synchronized Dimension getValues(){   
  2. return new Dimension (d.x, d.y);   
  3. } 

现在,Example对象的内部状态就安全了。调用者可以根据需要改变它所得到的拷贝的状态,但是要修改Example对象的内部状态,必须通过setValues()才可以。

三、常见错误3#:不必要的克隆

我们现在知道了get方法应该返回内部数据对象的拷贝,而不是引用。但是,事情没有绝对:

  1. /*** Example class.The value should never * be negative.*/   
  2. public class Example{   
  3.   private Integer i = new Integer (0);   
  4.   public Example (){ }  
  5.   /*** Set x. x must be nonnegative* or an exception will be thrown*/   
  6.   public synchronized void setValues (int x) throws IllegalArgumentException{   
  7. if (x < 0)   
  8.   throw new IllegalArgumentException();   
  9.   i = new Integer (x);   
  10.   }  
  11.   public synchronized Integer getValue(){   
  12. // We can’t clone Integers so we makea copy this way.   
  13. return new Integer (i.intValue());   
  14.   }   
  15. } 

这段代码是安全的,但是就象在错误1#那样,又作了多余的工作。Integer对象,就象String对象那样,一旦被创建就是不可变的。因此,返回内部Integer对象,而不是它的拷贝,也是安全的。

方法getValue()应该被写为:

  1. public synchronized Integer getValue(){   
  2. // ’i’ is immutable, so it is safe to return it instead of a copy.   
  3. return i;   
  4. } 

Java程序比C++程序包含更多的不可变对象。JDK 所提供的若干不可变类包括:

  • Boolean
  • Byte
  • Character
  • Class
  • Double
  • Float
  • Integer
  • Long
  • Short
  • String
  • 大部分的Exception的子类

四、常见错误4# :自编代码来拷贝数组

Java允许你克隆数组,但是开发者通常会错误地编写如下的代码,问题在于如下的循环用三行做的事情,如果采用Object的clone方法用一行就可以完成:

  1. public class Example{   
  2. private int[] copy;   
  3. /*** Save a copy of ’data’. ’data’ cannot be null.*/   
  4. public void saveCopy (int[] data){   
  5. copy = new int[data.length];   
  6. for (int i = 0; i < copy.length; ++i)   
  7. copy[i] = data[i];   
  8. }   
  9. } 

这段代码是正确的,但却不必要地复杂。saveCopy()的一个更好的实现是:

  1. void saveCopy (int[] data){   
  2. try{   
  3. copy = (int[])data.clone();   
  4. }catch (CloneNotSupportedException e){   
  5. // Can’t get here. }   
  6. } 

如果你经常克隆数组,编写如下的一个工具方法会是个好主意:

  1. static int[] cloneArray (int[] data){   
  2. try{   
  3. return(int[])data.clone();   
  4. }catch(CloneNotSupportedException e){   
  5. // Can’t get here.   
  6. }   
  7. } 

这样的话,我们的saveCopy看起来就更简洁了:

  1. void saveCopy (int[] data){   
  2. copy = cloneArray ( data); } 

五、常见错误5#:拷贝错误的数据

有时候程序员知道必须返回一个拷贝,但是却不小心拷贝了错误的数据。由于仅仅做了部分的数据拷贝工作,下面的代码与程序员的意图有偏差:

  1. import java.awt.Dimension;   
  2. /*** Example class. The height and width values should never * be   
  3. negative. */   
  4. public class Example{   
  5. static final public int TOTAL_VALUES = 10;   
  6. private Dimension[] d = new Dimension[TOTAL_VALUES];   
  7. public Example (){ }  
  8. /*** Set height and width. Both height and width must be nonnegative * or
  9.  an exception will be thrown. */   
  10. public synchronized void setValues (int index, int height, int width) 
  11. throws IllegalArgumentException{   
  12. if (height < 0 || width < 0)   
  13. throw new IllegalArgumentException();   
  14. if (d[index] == null)   
  15. d[index] = new Dimension();   
  16. d[index].height = height;   
  17. d[index].width = width;   
  18. }  
  19. public synchronized Dimension[] getValues()   
  20. throws CloneNotSupportedException{   
  21. return (Dimension[])d.clone();   
  22. }   
  23. } 

这儿的问题在于getValues()方法仅仅克隆了数组,而没有克隆数组中包含的Dimension对象,因此,虽然调用者无法改变内部的数组使其元素指向不同的Dimension对象,但是调用者却可以改变内部的数组元素(也就是Dimension对象)的内容。方法getValues()的更好版本为:

  1. public synchronized Dimension[] getValues() throws CloneNotSupportedException{   
  2. Dimension[] copy = (Dimension[])d.clone();   
  3. for (int i = 0; i < copy.length; ++i){   
  4. // NOTE: Dimension isn’t cloneable.   
  5. if (d != null)   
  6. copy[i] = new Dimension (d[i].height, d[i].width);   
  7. }  
  8. return copy;   
  9. } 

在克隆原子类型数据的多维数组的时候,也会犯类似的错误。原子类型包括int,float等。简单的克隆int型的一维数组是正确的,如下所示:

  1. public void store (int[] data) throws CloneNotSupportedException{   
  2. this.data = (int[])data.clone();   
  3. // OK   
  4. } 

拷贝int型的二维数组更复杂些。Java没有int型的二维数组,因此一个int型的二维数组实际上是一个这样的一维数组:它的类型为int[]。简单的克隆int[][]型的数组会犯与上面例子中getValues()方法第一版本同样的错误,因此应该避免这么做。下面的例子演示了在克隆int型二维数组时错误的和正确的做法:

  1. public void wrongStore (int[][] data) throws CloneNotSupportedException{   
  2.   this.data = (int[][])data.clone(); // Not OK!   
  3. }  
  4. public void rightStore (int[][] data){   
  5.   // OK!   
  6.   this.data = (int[][])data.clone();   
  7.   for (int i = 0; i < data.length; ++i){   
  8. if (data != null)   
  9.   this.data[i] = (int[])data[i].clone();   
  10.   }   
  11. } 
以上都是转载的东西,作为提醒自己以后注意点东西,最后加点自己的看法,虽然肤浅
对于上面的#2, 3, 5 点,我认为主要问题就是getValue() 获取的是对象引用, 在Effective Java 上面有比较详细的讲述,可不可写一个工具类,实现对象的深度拷贝呢,这样这些问题就不存在了。
对于#4, Arrays已经存在这样的拷贝数组方法了,可以不去发明新的轮子吧.

Java对象克隆深入解析与实践指南 在Java编程中,对象克隆是创建一个与原对象状态相同的新对象的过程。这一概念对于理解对象复制的机制至关重要,它允许开发者在不直接使用构造函数的情况下快速复制对象。克隆分为浅克隆和深克隆两种类型,它们根据复制的深度不同而有显著的区别。理解这两种克隆技术的原理和实现方式,对于设计高质量、可维护和高效的代码至关重要。在接下来的章节中,我们将深入探讨浅克隆与深克隆的区别和实现方法,以及如何通过实现Cloneable接口和自定义克隆逻辑来满足复杂对象的克隆需求。 阅读详情

相关推荐

别再用RIDE了!VSCode+RobotFramework插件打造现代化测试开发环境

本文介绍了如何利用VSCode和RobotFramework插件构建现代化的测试开发环境,替代老旧的RIDE工具。详细讲解了环境配置、插件安装、智能代码补全、测试运行与调试等核心功能,帮助开发者提升自动化测试效率。文章还提供了高级技巧、最佳实践和常见问题解决方案,助力团队实现平滑迁移。

weixin_28829629的博客 480

虚拟机克隆+虚拟机网络问题排查

解决报错Failed to start LSB: Bring up/down networking:MAC地址导致 虚拟机网络连接 一、前提:ifconfig 查看网卡发现未开启ens33网卡 Ens33网卡在一般新部署的Centos8或Centos7系统中都是关闭的,我们需要手动打开它。而Ens33网卡不显示IP地址多数是因为Network服务存在问题导致。所以我们首先要打开ens33,然后再解决ip问题。 输入ifconfig ens33 up(或nmcli c up ens33)后再次输入ifc

qq_35024768的博客 1508

jmeter问题系列:Begin size 0 is not equal to fixed size 5

Begin size 0 is not equal to fixed size 5 ,问题的解决

雨水的早晨的博客 2415

七、克隆虚拟机、常见错误及解决方案

解决办法:需要打开笔记本 BIOS 中的 IVT 对虚拟化的支持。问题原因:宿主机 BIOS 设置中的硬件虚拟化被禁用了。

qq_45735705的博客 2881

【C++】POCO学习总结(六):线程、线程池、同步

Poco::RunnableAdapter继承自Poco::Runnable,在Poco::RunnableAdapter构造函数中指定了类和方法;Poco::ThreadLocal是线程局部变量,也叫线程局部存储,意思是模版ThreadLocal中填充的变量属于当前线程,该变量对其他线程而言是隔离的,也就是说该变量是当前线程独有的变量。

郭老二 5100

机械臂抓取前言

机械臂相关的笔记

weixin_64037619的博客 3908

程序员必知:Java代码常见的十种错误

每一个程序员在编写代码的过程中都免不了出现错误或是小的失误,这些小的错误和失误往往使得程序员还得返工。那么,如何才能尽量避免这些错误的发生呢?笔者总结只有在日常的编写代码中总结出经验,在这篇文章中,笔者列出了10个Java编程中常见的错误,你可以把这些错误添加到你的代码审查的检查

爱是一种信仰 1863

Android 13 内置xapk安装包的方法

安装xapk 内置xapk

安卓兼职framework和app工程师的博客 224

linux虚拟机系统的复制或克隆后续问题解决!

linux虚拟机系统的复制或克隆后续问题解决!

dearbaba_11的博客 235

SAP 公司间采购简介

SAP公司间采购(Intercompany Procurement)是指企业集团内部不同法人实体之间的采购和销售流程。这种流程在跨国公司或有多个独立运营子公司的企业中非常常见。SAP提供了标准的功能来处理公司间采购,确保财务和物流流程的透明性和效率。以下是SAP公司间采购的基本流程及其相关模块的简介。

我小时候很黑的博客 1378

FPGA电路设计1——芯片原理图和封装制作流程

本文详细介绍了FPGA电路板设计的学习过程,以Xilinx Kintex-7系列FPGA为例。主要内容包括:1)从官网下载芯片引脚分配文件的方法;2)原理图库绘制流程,重点讲解Bank划分(配置Bank、HR/HP IO Bank、高速收发器Bank)及利用Excel预处理数据;3)使用Altium Designer的Symbol Wizard工具实现引脚自动化导入;4)原理图设计要点,包括电源系统设计(多电压需求与上电时序)、配置电路(JTAG/Flash接口)和时钟设计。

在读研究生,FPGA电路设计与功能开发。 1385

linux 克隆及 错误

玉凤 http://www.cnblogs.com/xiaoerlang/p/3719275.html http://www.aminglinux.com/study_v2/ vi进入后,按i变成编辑模式,屏幕下方有个INSERT字样,这时跟windows的记事本一样了,改吧。内容见下 改完后按ESC键,再输入冒号x——:x保存退出。 service network restart ...

yuhui666666的博客 428

基于Spring Boot 3 + Vue 3 + Vite的现代化社区订餐系统设计与实现

本文将详细阐述系统的整体架构、数据库设计、核心功能模块(用户管理、餐厅管理、菜品浏览、购物车、订单管理)的实现细节,并提供关键代码示例。<el-input v-model="loginForm.password" type="password" placeholder="密码" prefix-icon="Lock" show-password />.requestMatchers("/api/auth/**", "/api/dishes/public/**").permitAll() // 公开接口。

2201_76125393的博客 468

注解与对象克隆

注解,元注解,内置注解,对象克隆及其实现,浅克隆和深克隆

m0_61976036的博客 976

温度PID控制PLC程序

温度PID稳定控制程序,适用于欧姆龙PLC,对其他PLC也具有很大参考价值

Camera2Demo

在Camera2 的基础上进行封装,使得Camera2一堆初始化逻辑与Activity分离,降低耦合度,完成预览拍照并保存的操作

深入理解Java对象克隆:从浅入深掌握深克隆与浅克隆

为什么需要对象克隆? 在Java开发中,对象克隆是一个看似简单却暗藏玄机的技术。 直接使用=赋值操作符仅复制引用,如同给同一把锁配多把钥匙,任何一个钥匙开锁都会改变锁的状态。 真正的克隆需要创建对象的"平行宇宙"版本——这就是深浅克隆的本质区别。 对象克隆是Java开发中的双刃剑,正确使用能显著提升系统安全性和性能,滥用则可能导致内存泄漏和数据混乱。

阿新的博客 1452

蓝牙模块HM-10手册

蓝牙模块HM-10手册,该手册对蓝牙模块HM-10进行了详细的介绍和使用方法以及一些AT指令。

JavaScript对象克隆

浅克隆 let clone = Object.assign({}, target) 深克隆 简单的深克隆可以使用JSON.parse()和JSON.stringify(),这两个api是解析json数据的,所以只能解析除symbol外的原始类型及数组和对象 let clone = JSON.parse( JSON.stringify(target) ) 深度克隆,这里列出了原始类型,时间、正则...

qq_35965441的博客 219

廖雪峰Python教程[完整版]

廖雪峰的python教程整理的,已经排好版了,而且还是完整的,适合小白

java对象克隆

前言:前几天在做题的时候,其中我定义一个数组arr1,将结果值存入该数组,然后因为需要在数组中查找,为了提高查询效率,需要将该数组的元素进行排序,但是还需要保留原顺序的数组,于是高能来了,直接int[] arr2=arr1;将arr2排序完之后,再进行接下来的运算...结果发现测试数据结果不对,debug环境下看,结果arr1顺序不是原来的顺序了,恍然大悟犯了一个低级错误,int[] arr2=arr1这种方式是将arr1的引用赋值给arr2,arr1和arr2指向内存堆的同一个对象,如果去对arr2进行改

Slinxiner的博客 275

PHP 如何克隆一个新对象,php克隆对象是什么?它是如何实现的?

有的时候我们需要在一个项目里面,使用两个或多个一样的对象,如果你使用“new” 关键字重新创建对象的话,再赋值上相同的属性,这样做比较烦琐而且也容易出错,所以要 根据一个对象完全克隆出一个一模一样的对象,是非常有必要的,而且克隆以后,两个对象 互不干扰。在PHP5 中我们使用“clone”这个关键字克隆对象; ...

weixin_32515577的博客 210

js对象克隆

js 对象克隆可以分为浅克隆和深克隆两种方式。 基本数据类型不管是浅克隆还是深克隆都是对值本身的克隆,对克隆后值的修改不会影响到原始值。 引用数据类型如果执行的是浅克隆,对克隆后值的修改会影响到原始值;如果执行的是深克隆,则克隆的对象和原始对象相互独立,不会彼此影响。 首先来说浅克隆 1.简单的引用复制 2.ES6新发布的Object.assign() 函数来实现浅克隆 js对象深克隆的有多种实现方法 1.JSON序列化和反序列化 当我们修改obj的键值对时不会影响result..

wx479的博客 1152

Unity3D 入门小技巧——克隆游戏对象与创建预制

克隆游戏对象与创建预制

SYSUJackJiao的博客 2万+

浅克隆以及深克隆到底什么意思

一、这个克隆真的是克隆而已吗? 没错,就是你想像的克隆一个对象,依据已经有的数据,创造一份新的完全一样的数据拷贝 二、他们有什么区别吗? 一、两大区别,浅克隆和深克隆 浅克隆:是指拷贝对象时仅仅拷贝对象本身(包括对象中的基本变量),而不拷贝对象包含的引用指向的对象,也就是说对象虽然克隆出来了,但是引用的对象还是那个。 深克隆:不仅拷贝对象本身,而且拷贝对象包含的引用指向的所有对象。 四、克隆有什么作用? 看一下下面这段代码 public static void main(String[] arg

TwelveT的博客 1697

克隆对象、对象继承

1.克隆对象 这种写法不是对象克隆,就是把obj的内存地址赋给了obj2(这是错误的写法) (1)通过for in克隆,不管是私有的还是公有的全部克隆成私有的 (2)js提供了一个一个克隆的方法 Object.create()   var obj2=Object.create() 将obj的所有的属性克隆到obj2的原型上    2.对象的继承 (1)原型继...

168

设计模式|原型模式的使用案例、克隆复合对象出现的问题及解决方案、深克隆和浅克隆的区别

目录 原型模式的使用案例 操作复合对象出现的问题及解决方案 深克隆和浅克隆的区别 原型模式的使用案例 原型模式是创建型模式之一,使用原型模式创建对象比直接new对象的性能高、简化创建过程。原型模式的使用较为简单,具体实现如下 public class Group implements Cloneable{ private String groupName; publ...

leo187的博客 523

Java—对象的克隆

在上面的示例中,我们定义了一个Student类,并实现了Cloneable接口。在Main类中,我们创建了一个Student对象student1,并通过调用clone()方法创建了student2作为其副本。最后,我们修改了student2的属性,并打印出两个对象的值,可以看到它们是相互独立的。这种方式可以确保整个对象的所有属性都被正确地复制,但需要注意的是,序列化和反序列化会对性能产生较大的影响。在Java中,对象的克隆是通过实现Cloneable接口和重写clone()方法来实现的。

qq_56725871的博客 562

Java注解&对象克隆

注解是java中的标注方式,可以作用在类,方法,变量,参数成员上在编译期间,会被编译到字节码文件中,运行时通过反射机制获得注解内容,进行解析在Java中,对象克隆是一个很常见的操作,它允许你创建一个对象的副本。默认情况下,Java中的对象克隆是浅拷贝(shallow copy),这意味着如果对象中的任何字段是引用类型,那么这两个对象将会共享这些引用。要克隆一个对象,首先需要实现 Cloneable 接口,然后重写 Object 类的 clone() 方法。return age;

谜语人的博客 429

前端 js深度克隆对象数组

在项目中使用一个对象,但是这个对象里面修改一些属性的话会造成之前的数据的一些错误,所以我决定深度克隆一些,但是现在谁还自己写呀,所以我果断使用插件, 使用了插件的我,腰也不疼了,腿也不酸了,真香,但是使用完之后我深深的陷入了悔恨,咋这么不争气那,不会自己写一个啊,人与人之间的距离就是这样拉开的,我是开车的,人家是造车的。555555555555555555!!! 所以我决定要自己 写一段代码 function deepClone ( obj ) { let clone = Array.

平凡的世界的博客 408
下一篇: Joda Study
Wo_Ai_Java
博客等级 码龄16年 26粉丝 20原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值