java clone 对象_为什么阿里Java手册推荐慎用 Object 的 clone 方法来拷贝对象
前言
在阿里Java开发手册中,有这么一条建议:慎用 Object 的 clone 方法来拷贝对象。对象 clone 方法默认是浅拷贝,若想实现深拷贝需覆写 clone 方法实现域对象的深度遍历式拷贝 。Java中的对象拷贝,有浅拷贝和深拷贝两种,如果没有搞清楚这两者的区别,那么可能会给自己的代码埋下隐患。
什么是浅拷贝和深拷贝
浅拷贝:被复制对象的所有变量都含有与原来的对象相同的值,而所有的对其他对象的引用仍然指向原来的对象。换言之,浅复制仅仅复制所考虑的对象,而不复制它所引用的对象。
深拷贝:被复制对象的所有变量都含有与原来的对象相同的值,除去那些引用其他对象的变量。那些引用其他对象的变量将指向被复制过的新对象,而不再是原有的那些被引用的对象。换言之,深复制把要复制的对象所引用的对象都复制了一遍。
通过上面的结论,我们可以看出浅拷贝和深拷贝的区别就在于所要拷贝的对象的引用数据类型,如果是拷贝一份引用,那么这是浅拷贝,如果是新建一个对象,那么这就是深拷贝。
clone方法
在Java的Object对象中,有clone这个方法。它被声明为了 protected,所以我们可以在其子类中使用它。这里需要注意的是,我们在子类中使用clone方法时,子类需要实现Cloneable接口,否则会抛出java.lang.CloneNotSupportedException异常。
有如下对象
如下实体类都使用了Lombok。
Address.java
@Data
public class Address{
private String address;
}
复制代码
Person.java
@Data
public class Person implements Cloneable{
private String name;
private Integer age;
private Address address;
@Override
protected Object clone() throws CloneNotSupportedException{
return super.clone();
}
}
复制代码
浅拷贝
浅拷贝,示例代码如下:
public static void main(String[] args) throws CloneNotSupportedException{
Person person = new Person();
person.setName("Happyjava");
person.setAge(33);
Address address = new Address();
address.setAddress("浙江杭州");
person.setAddress(address);
Person newPerson = (Person) person.clone();
System.out.println(person == newPerson);
System.out.println(person.getAddress() == newPerson.getAddress());
}
复制代码
通过 == 比较是否是同一个对象。其运行结果如下:
false
true
复制代码
说明了通过clone方法拷贝出来的对象,与原对象并不是同一个对象。而person.getAddress() == newPerson.getAddress() 的比较是true,说明了二者的引用都是指向同一个对象。这就是浅拷贝,引用类型还是指向原来的对象。
浅拷贝存在的问题
很多时候,我们拷贝一个对象,是希望完全进行深度拷贝的。浅拷贝存在的问题就是,对于原对象引用类型的属性进行修改,拷贝出来的对象也会受到影响(因为二者的引用都指向同一个对象)。如下代码:
public static void main(String[] args) throws CloneNotSupportedException{
Person person = new Person();
person.setName("Happyjava");
person.setAge(33);
Address address = new Address();
address.setAddress("浙江杭州");
person.setAddress(address);
Person newPerson = (Person) person.clone();
newPerson.getAddress().setAddress("广东省深圳市");
System.out.println(person.getAddress().getAddress());
}
复制代码
运行结果如下:
通过newPerson把address设置为“广东省深圳市”,person的address也变成了"广东省深圳市"。
这种情况,如果我们没有注意,是很容易造成生产事故的。
深拷贝
通过clone方法实现深拷贝,是一件比较麻烦的事情,因为我们需要手动在clone方法里拷贝引用类型。代码修改如下:
Address.java
@Data
public class Address implements Cloneable{
private String address;
@Override
protected Object clone() throws CloneNotSupportedException{
return super.clone();
}
}
复制代码
Person.java
@Data
public class Person implements Cloneable{
private String name;
private Integer age;
private Address address;
@Override
protected Object clone() throws CloneNotSupportedException{
Person newPerson = (Person) super.clone();
newPerson.address = (Address) this.address.clone();
return newPerson;
}
}
复制代码
通过clone方法实现深拷贝,我们需要在Person的clone方法里调用address的clone方法,并且手动设置clone出来的新的address。
再次执行上面的测试代码,运行结果如下:
通过序列化实现深拷贝
通过clone方法实现深拷贝是比较麻烦的一件事情,这里推荐大家可以通过序列化、反序列化的方式实现深拷贝。我们可以直接使用commons-lang3包的序列化、反序列工具类。
引入依赖
org.apache.commons
commons-lang3
3.8.1
复制代码
序列化需要实现Serializable接口,Person和Address类都需要实现。测试代码如下:
public static void main(String[] args) throws CloneNotSupportedException{
Person person = new Person();
person.setName("Happyjava");
person.setAge(33);
Address address = new Address();
address.setAddress("浙江杭州");
person.setAddress(address);
// 序列化
byte[] serialize = SerializationUtils.serialize(person);
// 反序列化
Person newPerson = SerializationUtils.deserialize(serialize);
System.out.println(person == newPerson);
System.out.println(person.getAddress() == newPerson.getAddress());
}
复制代码
运行结果如下:
通过结果可以看出,反序列化构建出来的对象,是全新的、深度拷贝的。
总结
拷贝对象,如果直接通过clone方法进行拷贝,是很容易出现问题的。我们要清楚的知道浅拷贝和深拷贝的区别。
原创声明
本文发布于掘金号【Happyjava】。Happy的掘金地址:juejin.im/user/398428…,Happy的个人博客:blog.happyjava.cn。欢迎转载,但须保留此段声明。
java clone 对象_为什么阿里Java手册推荐慎用 Object 的 clone 方法来拷贝对象相关推荐
- java代码内创建mysql索引_点评阿里JAVA手册之MySQL数据库 (建表规约、索引规约、SQL语句、ORM映射)...
下载原版阿里JAVA开发手册 [阿里巴巴Java开发手册v1.2.0] 本文主要是对照阿里开发手册,注释自己在工作中运用情况. 本文内容:MySQL数据库 (建表规约.索引规约.SQL语句.ORM映 ...
- serialversionuid的作用_为什么阿里Java规约要求谨慎修改serialVersionUID字段
serialVersionUID简要介绍 serialVersionUID是在Java序列化.反序列化对象时起作用的一个字段.Java的序列化机制是通过判断类的serialVersionUID来验证版 ...
- java通用对象_有效的Java –所有对象通用的方法
java通用对象 所有对象共有的方法(第3章) 这是Joshua Blochs的有效Java第3章的简短摘要.我仅包含与自己相关的项目. 一般 等值合约将等价关系描述为: x.equals(null) ...
- java clone原理_详解Java中的clone方法 -- 原型模式
Java中对象的创建 clone顾名思义就是复制, 在Java语言中, clone方法被对象调用,所以会复制对象.所谓的复制对象,首先要分配一个和源对象同样大小的空间,在这个空间中创建一个新的对象.那 ...
- java创建一个不可变对象_如何在Java中创建不可变类?
java创建一个不可变对象 Today we will learn about the immutable class in Java. What are immutable classes? The ...
- string转成对象_详解Java I/O流(五),对象序列化
对象序列化 什么是序列化和反序列化呢? 序列化就是将对象转成字节序列的过程,反序列化就是将字节序列重组成对象的过程. 在这里插入图片描述 为什么要有对象序列化机制 程序中的对象,其实是存在有内存中,当 ...
- JAVA类思维_面向对象思维 Java中的类和对象及其应用
一.面向过程与面向对象 面向过程: 从事务执行者的角度思考问题,我该干什么 重点在过程----事务流程 面向对象: 从事务的指挥者角度思考问题,我应该找谁干什么 重点在对象 面向对象的优点: 1. ...
- 传递集合对象_面试必备——Java集合框架
Java集合框架面试题 常见集合 集合可以看作是一种容器,用来存储对象信息. 数组和集合的区别: (1)数组长度不可变化而且无法保存具有映射关系的数据:集合类用于保存数量不确定的数据,以及保存具有映射 ...
- java 并发变量_二、Java多线程编程 (对象及变量的并发访问)
非线程安全 多个线程对同一个对象中的实例变量进行并发操作时会出现值被更改.值不同步的情况,进而影响程序的执行流程. 线程安全 线程安全就是获得实例变量的值是经过同步处理的.不会出现被更改不同步的情况. ...
- java json转抽象对象_做一次面向对象的体操:将 JSON 字符串转换为嵌套对象的一种方法...
原标题:做一次面向对象的体操:将 JSON 字符串转换为嵌套对象的一种方法 来源:琴水玉 , www.cnblogs.com/lovesqcc/p/9478678.html 程序员共读整理发布,转载请 ...
最新文章
- Jupyter Notebook 添加目录
- pwn学习总结(五) —— ret2dl_runtime_resolve(待补充)
- 关于 early Z 与 z-prepass
- nullable field verification in gateway backend
- 作者:许会泉,男,北京金信网银金融信息服务有限公司研发总监。
- Java Web-网页基础-HTML-选择器Selector-DOM
- 2015计算机类专业课类试卷,2015年自考《计算机应用基础》模拟试题及答案
- Java自动装箱和拆箱
- C++表白代码--Beating heart
- 人工智能TensorFlow工作笔记004---还记得标准差嘛_标准差的由来
- ActiveMQ - spring集成jms
- 大创笔记——硬切法实现基于单片机的人机交互系统
- java 打砖块算法_打砖块java代码详细
- 【数据结构】有限状态自动机(FSA)的理解-LeetCode表示数值的字符串(有效数字)题解(Java)
- 揭秘北京龙泉寺,连清华北大学子都排队出家的神秘科研组织
- 面对这个缓慢、脆弱、健忘的互联网,IPFS协议势在必行!
- IMWEB小白DAY3-制作个人名片
- 深度学习花书学习感悟之第二章线性代数
- 关于blob数据类型引起的mysqldump乱码问题
- 【shell命令】拆分、合并、排序、比较文件
热门文章
- 给出n元置换群线性表示的一种方法
- 论合伙企业相对有限责任公司的比较优势
- cdr软件百度百科_cdr软件是什么?cdr是什么软件?
- 计算机cpu结构实物图片,cpu内部结构显微图/cpu内部结构放大图
- 专升本英语作文信件类必背范文十篇
- 2017年5月14日爱奇艺算法比赛
- 黑马程序员-----视频看完了,谈谈自己的感受
- 怎么把知网的外文文献翻译成中文_中国知网上的汉语文献的英文版在哪里找
- html数据透视,Excel数据透视表使用过程中常见问题 如何在excel数据透视表中使用函数公式...
- 万物互联会改变什么?