写在最前面,我总结出了很多互联网公司的面试题及答案,并整理成了文档,以及各种学习的进阶学习资料,免费分享给大家。扫码加微信好友进【程序员面试学习交流群】,免费领取。也欢迎各位一起在群里探讨技术。

文章同步更新在个人博客:关于Java的Object.clone()方法与对象的深浅拷贝

引言

在某些场景中,我们需要获取到一个对象的拷贝用于某些处理。这时候就可以用到Java中的Object.clone方法进行对象复制,得到一个一模一样的新对象。但是在实际使用过程中会发现:当对象中含有可变的引用类型属性时,在复制得到的新对象对该引用类型属性内容进行修改,原始对象响应的属性内容也会发生变化,这就是"浅拷贝"的现象。关于浅拷贝,Object.clone()方法的描述也有说明:


/*** Creates and returns a copy of this object.  The precise meaning* ...* The method {@code clone} for class {@code Object} performs a* specific cloning operation. First, if the class of this object does* not implement the interface {@code Cloneable}, then a* {@code CloneNotSupportedException} is thrown. Note that all arrays* are considered to implement the interface {@code Cloneable} and that* the return type of the {@code clone} method of an array type {@code T[]}* is {@code T[]} where T is any reference or primitive type.* Otherwise, this method creates a new instance of the class of this* object and initializes all its fields with exactly the contents of* the corresponding fields of this object, as if by assignment; the* contents of the fields are not themselves cloned. Thus, this method* performs a "shallow copy" of this object, not a "deep copy" operation.** 上面这里已经说明了,clone()方法是浅拷贝,而不是深拷贝** ...* @return     a clone of this instance.* @exception  CloneNotSupportedException  if the object's class does not*               support the {@code Cloneable} interface. Subclasses*               that override the {@code clone} method can also*               throw this exception to indicate that an instance cannot*               be cloned.* @see java.lang.Cloneable*/protected native Object clone() throws CloneNotSupportedException;

浅拷贝

接下来用代码看看浅拷贝的效果。

1. 创建一个Person类


package com.test.objclone.shallow;public class Person implements Cloneable {private int age;private String name;private Address address;public Person(int age, String name, Address address) {this.age = age;this.name = name;this.address = address;}@Overrideprotected Object clone() throws CloneNotSupportedException {return super.clone();}public int getAge() {return age;}public void setAge(int age) {this.age = age;}public String getName() {return name;}public void setName(String name) {this.name = name;}public Address getAddress() {return address;}public void setAddress(Address address) {this.address = address;}public String display() {return "Person [age=" + age + ", name=" + name + ", address=" + address + "]";}}

由于clone()方法是protected修饰的,因此需要实现Cloneable接口才能调用,同时需要覆写clone()方法才能调用。

2. 创建一个Address类


package com.test.objclone.shallow;public class Address {private String province;private String street;public Address(String province, String street) {this.province = province;this.street = street;}public String getProvince() {return province;}public void setProvince(String province) {this.province = province;}public String getStreet() {return street;}public void setStreet(String street) {this.street = street;}@Overridepublic String toString() {return "Address [province=" + province + ", street=" + street + "]";}}

3. 浅拷贝测试


package com.test.objclone.shallow;/*** 浅拷贝测试* </ul>* @author hanxiaojun* @version 5.0 since 2018年3月14日*/public class MTest {public static void main(String[] args) throws CloneNotSupportedException {Person person = new Person(15, "zhangsan", new Address("四川", "天府二街"));Person clonePerson = (Person) person.clone();System.out.println(person);System.out.println(clonePerson);System.out.println(person.display());System.out.println(clonePerson.display());clonePerson.setName("wangwu");clonePerson.setAge(20);Address address = clonePerson.getAddress();address.setStreet("天府四街");System.out.println(clonePerson.display());System.out.println(person.display());}}

以上程序结果如下:

com.test.objclone.shallow.Person@7ba28183com.test.objclone.shallow.Person@69e4fedePerson [age=15, name=zhangsan, address=Address [province=四川, street=天府二街]]Person [age=15, name=zhangsan, address=Address [province=四川, street=天府二街]]Person [age=20, name=wangwu, address=Address [province=四川, street=天府四街]]Person [age=15, name=zhangsan, address=Address [province=四川, street=天府四街]]

可以看到:

第1、2句输出说明了原对象与新对象是两个不同的对象。

第3、4句可以看到拷贝出来的新对象与原对象内容一致

但是,接着将新对象里面的信息进行了修改,然后输出发现原对象里面的部分信息也跟着变了。仔细观察发现原对象跟着变化的只是Address部分,这就跟clone本身的浅拷贝有关系了。

浅拷贝:创建一个新对象,然后将当前对象的非静态字段复制到该对象,如果字段类型是值类型(基本类型)的,那么对该字段进行复制;如果字段是引用类型的,则只复制该字段的引用而不复制引用指向的对象。此时新对象里面的引用类型字段相当于是原始对象里面引用类型字段的一个副本,原始对象与新对象里面的引用字段指向的是同一个对象。

因此,修改clonePerson里面的address内容时,原person里面的address内容会跟着改变。

深拷贝

了解了浅拷贝,那么深拷贝是什么也就很清楚了。即将引用类型的属性内容也拷贝一份新的。

那么,实现深拷贝我这里收集到两种方式:第一种是给需要拷贝的引用类型也实现Cloneable接口并覆写clone方法;第二种则是利用序列化。接下来分别对两种方式进行演示。

深拷贝-clone方式

对于以上演示代码,利用clone方式进行深拷贝无非就是将Address类也实现Cloneable,然后对Person的clone方法进行调整。

1. Address类变动

实现Cloneable,并覆写clone方法


package com.test.objclone.deep.clone;public class Address implements Cloneable{private String province;private String street;public Address(String province, String street) {this.province = province;this.street = street;}@Overrideprotected Object clone() throws CloneNotSupportedException {return super.clone();}public String getProvince() {return province;}public void setProvince(String province) {this.province = province;}public String getStreet() {return street;}public void setStreet(String street) {this.street = street;}@Overridepublic String toString() {return "Address [province=" + province + ", street=" + street + "]";}}

2. Person类变动

对clone方法进行如下调整:


package com.test.objclone.deep.clone;public class Person implements Cloneable {private int age;private String name;private Address address;public Person(int age, String name, Address address) {this.age = age;this.name = name;this.address = address;}@Overrideprotected Object clone() throws CloneNotSupportedException {Person person = (Person)super.clone();//手动对address属性进行clone,并赋值给新的person对象person.address = (Address) address.clone();return person;}public int getAge() {return age;}public void setAge(int age) {this.age = age;}public String getName() {return name;}public void setName(String name) {this.name = name;}public Address getAddress() {return address;}public void setAddress(Address address) {this.address = address;}public String display() {return "Person [age=" + age + ", name=" + name + ", address=" + address + "]";}}

3. 深拷贝测试

再次执行上面的测试方法

节省篇幅,就不再贴主方法测试类了

此时,测试代码执行如下:

com.test.objclone.deep.clone.Person@69e4fedecom.test.objclone.deep.clone.Person@3918d722Person [age=15, name=zhangsan, address=Address [province=四川, street=天府二街]]Person [age=15, name=zhangsan, address=Address [province=四川, street=天府二街]]Person [age=20, name=wangwu, address=Address [province=四川, street=天府四街]]Person [age=15, name=zhangsan, address=Address [province=四川, street=天府二街]]

可以看到原person对象的address内容未受到影响。

但是,这种方法的缺点就是当一个类里面有很多引用类型时,需要手动调用很多clone,而且如果引用类型内部还有引用类型时,那么代码将会很恶心,量也很大。。。

所以这种方式一般用于引用类型变量较少的时候。

对于很多引用类型,可以使用序列化对象的方式进行深拷贝。

深拷贝-序列化方式

这种方式其实就是将对象转成二进制流,然后再把二进制流反序列成一个java对象,这时候反序列化生成的对象是一个全新的对象,里面的信息与原对象一样,但是所有内容都是一份新的。

这种方式需要注意的地方主要是所有类都需要实现Serializable接口,以便进行序列化操作。

1. 序列化-反序列化对象

先看看核心代码,序列化与反序列化对象:


package com.test.objclone.deep.serialize;import java.io.ByteArrayInputStream;import java.io.ByteArrayOutputStream;import java.io.ObjectInputStream;import java.io.ObjectOutputStream;import java.io.Serializable;public class DeepClone implements Serializable{private static final long serialVersionUID = 1L;/*** 利用序列化和反序列化进行对象的深拷贝* @return* @throws Exception*/protected Object deepClone() throws Exception{//序列化ByteArrayOutputStream bos = new ByteArrayOutputStream();ObjectOutputStream oos = new ObjectOutputStream(bos);oos.writeObject(this);//反序列化ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray());ObjectInputStream ois = new ObjectInputStream(bis);return ois.readObject();}}

2. Person类的修改

主要是实现Serializable接口,让对象支持序列化。


package com.test.objclone.deep.serialize;public class Person extends DeepClone{private static final long serialVersionUID = 1L;private int age;private String name;private Address address;public Person(int age, String name, Address address) {this.age = age;this.name = name;this.address = address;}public int getAge() {return age;}public void setAge(int age) {this.age = age;}public String getName() {return name;}public void setName(String name) {this.name = name;}public Address getAddress() {return address;}public void setAddress(Address address) {this.address = address;}public String display() {return "Person [age=" + age + ", name=" + name + ", address=" + address + "]";}}

3. Address类的修改

也需要实现Serializable接口,为了方便就继承DeepClone了。


package com.test.objclone.deep.serialize;public class Address extends DeepClone{private static final long serialVersionUID = 1L;private String province;private String street;public Address(String province, String street) {this.province = province;this.street = street;}public String getProvince() {return province;}public void setProvince(String province) {this.province = province;}public String getStreet() {return street;}public void setStreet(String street) {this.street = street;}@Overridepublic String toString() {return "Address [province=" + province + ", street=" + street + "]";}}

4. 测试类


package com.test.objclone.deep.serialize;public class MTest {public static void main(String[] args) throws Exception {Person person = new Person(15, "zhangsan", new Address("四川", "天府二街"));Person clonePerson = (Person) person.deepClone();System.out.println(person);System.out.println(clonePerson);System.out.println(person.display());System.out.println(clonePerson.display());clonePerson.setName("wangwu");clonePerson.setAge(20);Address address = clonePerson.getAddress();address.setStreet("天府四街");System.out.println(clonePerson.display());System.out.println(person.display());}}

测试代码执行结果如下:

com.test.objclone.deep.serialize.Person@43059849com.test.objclone.deep.serialize.Person@6a5bc8c9Person [age=15, name=zhangsan, address=Address [province=四川, street=天府二街]]Person [age=15, name=zhangsan, address=Address [province=四川, street=天府二街]]Person [age=20, name=wangwu, address=Address [province=四川, street=天府四街]]Person [age=15, name=zhangsan, address=Address [province=四川, street=天府二街]]

可见,对新对象clonePerson的修改并没影响原person对象的内容。

这便是由Object.clone()引出的深拷贝与浅拷贝,学习记录一下。

参考文章

  • 博客园-YSOcean-Java的深拷贝和浅拷贝

转载:https://www.cnblogs.com/nickhan/p/8569329.html

推荐内容:
Java面试题库及答案解析
java基础(七) java四种访问权限
十大经典排序算法最强总结(含JAVA代码实现)
JAVA工程师-蚂蚁金服电话面试
Java面试题:小白不得不懂的斐波那契数列
Java面试集合(一)
Java与c#的一些细节区别
BAT美团滴滴java面试大纲(带答案版)之四:多线程Lock
java主线程结束和子线程结束之间的关系
Java中级面试题——(上)

关于Java的Object.clone()方法与对象的深浅拷贝,java面试题,java初级笔试题相关推荐

  1. java数组的clone方法_深入浅出,如何更彻底地理解Java数组的clone方法

    说在前面 在进入理解clone前,我们需要对"基本数据类型"和"引用数据类型"的存储模式有一个清晰的认识. 基本数据类型,变量的内容保存的是实际的值:引用数据类 ...

  2. 详解Java中的clone方法 -- 原型模式

    Java中对象的创建 clone顾名思义就是复制, 在Java语言中, clone方法被对象调用,所以会复制对象.所谓的复制对象,首先要分配一个和源对象同样大小的空间,在这个空间中创建一个新的对象.那 ...

  3. java clone原理_详解Java中的clone方法 -- 原型模式

    Java中对象的创建 clone顾名思义就是复制, 在Java语言中, clone方法被对象调用,所以会复制对象.所谓的复制对象,首先要分配一个和源对象同样大小的空间,在这个空间中创建一个新的对象.那 ...

  4. 详解Java中的clone方法 -- 深拷贝和浅拷贝

    Java中对象的创建 clone顾名思义就是复制, 在Java语言中, clone方法被对象调用,所以会复制对象.所谓的复制对象,首先要分配一个和源对象同样大小的空间,在这个空间中创建一个新的对象.那 ...

  5. java 集合notifyall,java.lang.Object.notifyAll()方法实例

    全屏 java.lang.Object.notifyAll()唤醒正在等待此对象的监视器上的所有线程.通过调用wait方法的对象的监视器上的一个线程等待. 该唤醒线程将不能继续进行,直到当前线程放弃此 ...

  6. java.lang.CloneNotSupportedException: com.lbh.xxmanager.basic.alg.Node at java.lang.Object.clone

    问题描述: 使用Object的clone方法报错 该行代码报错 node = (Node) curr.clone(); java.lang.CloneNotSupportedException: co ...

  7. JS 中对象的深浅拷贝(ES3、ES5、ES6不同方法底层实现,一文搞清楚深浅拷贝面试常问题)

    JS 中对象的深浅拷贝   拷贝我们都知道这个词的意思,我们经常做过复制.粘贴的操作,其中的复制就是拷贝,那么在拷贝的时候,如果我们复制出来的内容和原内容是完全的分开,各自不相影响,那么这就属于深拷贝 ...

  8. 华大计算机面试题,华大基因java初级笔试题.docx

    华大基因java初级笔试题 姓名: 电话: PAGE4 / NUMPAGES4 华大基因-Java工程师笔试题基础题目java的基本数据类型有哪些?String是最基本的数据类型吗?Byte shor ...

  9. 初级java试题_java初级面试题有哪些?java初级面试题分享

    对于学习java专业的人来说,刚毕业或者工作1-2年是很适合做java初级程序员的,但是做程序员要想通过面试,就一定要在平时积累面试题,这样在面试的时候就可以提高胜算,那么今天我们就分享一些java初 ...

最新文章

  1. 手机mvno怎么设置_微信透明背景壁纸怎么弄 手机设置方法教程分享
  2. DBSAN密度聚类算法
  3. 开源项目几点心得,Java架构必会几大技术点
  4. 写 JSP 的痛点,真的非常痛!
  5. MacOS13系统升级动态壁纸无法安装解决方法
  6. 8,JESD204B协议介绍
  7. 几个项目管理的小故事
  8. 局域网,手机与电脑文件共享
  9. 网络游戏开发实战-坦克大战学习问题记录
  10. (一)CAD2014+VS2010+ObjectARX2014环境搭建(适用于非常非常小白的人)
  11. erlang send_after 源码剖析
  12. 注销手机号,存在重大安全问题
  13. Linux操作系统的基本命令
  14. Ubuntu14.04网易云音乐的下载及安装 ssh安装 卸载 安装输入法
  15. 卸载wps后桌面上的office文件图标变成了白色(亲测有效)
  16. java---implements
  17. 自发和诱发电生理活动之间的动态关系
  18. 机器学习算法(二十六):文本表示模型
  19. 计算机管理任务计划程序全部禁用,win10计划任务如何关闭_win10怎么禁用计划任务...
  20. 大学期间所有课设及大作业源代码

热门文章

  1. sql中数据类型的转换(自己写比较累哈,偷偷懒,转下别人的)
  2. c语言头文件_C语言学习之头文件的原理和使用方法
  3. 用户态程序阻塞原因_进程阻塞 操作系统某种情况进行进程的阻塞和唤醒操作...
  4. 突然不能访问服务器未响应,windows 访问不服务器未响应
  5. 分布式选举协议:Paxos
  6. TimingWheel[时间轮]介绍
  7. (45)Verilog HDL 秒灯电路设计
  8. STM32 定时器输出pwm的频率计算方法 PWM 频率检测方法 直流电机的位置控制
  9. popen() 函数 讲解
  10. STM32H7时钟树RCC分析---原理讲解(一)