谈谈java中对象的深拷贝与浅拷贝
知识点:java中关于Object.clone方法,对象的深拷贝与浅拷贝
引言:
在一些场景中,我们需要获取到一个对象的拷贝,这时候就可以用java中的Object.clone方法进行对象的复制,得到一个一模一样的新对象。(使用new再次创建一个相同的对象也是可以的,有些区别),当对象中含有可变的引用类型属性时,在复制得到的新对象对该引用类型属性内容进行修改,原始对象相应的属性内容也会发生变化,这就是"浅拷贝"的现象。反之原始对象相应的引用类型属性不发生变化,是 "深拷贝"。
浅拷贝
先看一个浅拷贝的demo实例
1.创建一个Person类
public class Person implements Cloneable{
private String name;
private int age;
private Address address;
//构造方法
public Person(String name, int age, Address address) {
this.name = name;
this.age = age;
this.address = address;
}
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
public String display(){
return "Person[name="+name+",age="+age+",address"+address+"]";
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public Address getAddress() {
return address;
}
public void setAddress(Address address) {
this.address = address;
}
}
由于clone()方法是protected修饰的,因此需要实现Cloneable接口才能调用,同时需要覆写clone()方法才能调用。
2.创建一个Address类
public class Address {
private String province;//省份
private String city;//所在城市
//构造方法
public Address(String province, String city) {
this.province = province;
this.city = city;
}
public String getProvince() {
return province;
}
public void setProvince(String province) {
this.province = province;
}
public String getCity() {
return city;
}
public void setCity(String city) {
this.city = city;
}
@Override
public String toString() {
return "Address[province="+province+",city="+city+"]";
}
}
3.浅拷贝测试
public class Test {
public static void main(String[] args) throws CloneNotSupportedException {
Person person=new Person("张三",20,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("李四");
clonePerson.setAge(22);
Address address=clonePerson.getAddress();
address.setProvince("江苏");
System.out.println(person.display());
System.out.println(clonePerson.display());
}
}
4.代码运行结果:

第1、2句输出说明了原对象与新对象是两个不同的对象。
第3、4句可以看到拷贝出来的新对象与原对象内容一致
但是,接着将新对象里面的信息进行了修改,然后输出发现原对象里面的部分信息也跟着变了。仔细观察发现原对象跟着变化的只是Address部分,这就跟clone本身的浅拷贝有关系了。
浅拷贝 :创建一个新对象,然后将当前对象的非静态字段复制到该对象,如果字段类型是值类型(基本类型)的,那么对该字段进行复制;如果字段是引用类型的,则只复制该字段的引用而不复制引用指向的对象。此时新对象里面的引用类型字段相当于是原始对象里面引用类型字段的一个副本,原始对象与新对象里面的引用字段指向的是同一个对象。
简而言之:类实现默认的Object.clone()方法,拷贝对象时,对于引用类型的成员变量(属性)拷贝只是拷贝“值”即地址(引用),没有在堆中开辟新的内存空间。
因此,修改clonePerson里面的address内容时,原person里面的address内容会跟着改变。
“深拷贝” :类重写clone()方法,对于引用类型成员变量,重新在堆中开辟新的内存空间,简单地说,将引用类型的属性内容也拷贝一份新的。
如果我们想实现深拷贝,有两种方法,第一种是给需要拷贝的引用类型也实现Cloneable接口并覆写clone方法;第二种则是利用序列化。
具体代码演示可参考:https://www.cnblogs.com/nickhan/p/8569329.html
接下来补充一下,涉及到的知识点
复制对象和复制引用的区别




String的特殊性性
其实string类型也是引用类型,不是基本类型,那上面的例子中,修改拷贝对象person的name后,原对象的name值并没有改变,这是什么原因呢?
答:其实string也是引用类型,在深复制时并没有进行单独复制,仅仅复制了引用,String并没有实现cloneable接口,之所以修改新克隆对象的name值后,
原对象的name对应的值没有改变,是因为String在内存中是不可以改变的对象,我们在修改新对象(或者原对象)的name值时,会新分配一块内存保存新修改的name值,新对象的引用(或者原对象的引用)会指向新的内存空间,原来的String 还会存在它的引用name,如果String的值不存在引用,会被回收,也因为原来的String值存在引用name,所以不会被回收,这样,可以解释上面的demo中,name虽然是String引用类型,但是修改name的值,并没有改变原对象的name值。
我们也可以在idea中,用debugger调试验证一下

执行上面代码前三行代码,debugger

上面的箭头可以看出person和clonePerson的name是指向同一对象的引用(@468)
修改其中的对象的name值debugger看看

当我们执行clonePerson.setName("李四");修改新克隆对象的name值以后,则:
新克隆对象的name引用对应的值,发生了改变,原对象的引用对应的内存值没有发生变化
克隆一个对象与new一个对象的区别
(1)java中clone和new都可以创建一个新的对象
(2)clone()不会调用构造方法,而new一个对象会调用类的构造方法
(3)clone()能快速创建一个已有对象的副本,即创建对象并且将已有对象中所有属性值克隆;new只能在JVM中申请一个空的内存区域,对象的属性值要通过构造方法赋值。
谈谈java中对象的深拷贝与浅拷贝的更多相关文章
- PHP中对象的深拷贝与浅拷贝
先说一下深拷贝和浅拷贝通俗理解 深拷贝:赋值时值完全复制,完全的copy,对其中一个作出改变,不会影响另一个 浅拷贝:赋值时,引用赋值,相当于取了一个别名.对其中一个修改,会影响另一个 PHP中, = ...
- 探究JS中对象的深拷贝和浅拷贝
深拷贝和浅拷贝的区别 在讲深拷贝和浅拷贝的区别之前,回想一下我们平时拷贝一个对象时是怎么操作的?是不是像这样? var testObj1 = {a: 1, b:2}, testObj2=testObj ...
- java 复制Map对象(深拷贝与浅拷贝)
java 复制Map对象(深拷贝与浅拷贝) CreationTime--2018年6月4日10点00分 Author:Marydon 1.深拷贝与浅拷贝 浅拷贝:只复制对象的引用,两个引用仍然指向 ...
- 谈谈JAVA中的安全发布
谈谈JAVA中的安全发布 昨天看到一篇文章阐述技术类资料的"等级",看完之后很有共鸣.再加上最近在工作中越发觉得线程安全性的重要性和难以捉摸,又掏出了<Java并发编程实战& ...
- 谈谈java中静态变量与静态方法在有继承关系的两个类中调用
谈谈java中静态变量与静态方法在有继承关系的两个类中调用 学习的中如果遇到不明白或者不清楚的的时候,就是自己做些测试,自己去试试,这次我就做一个关于静态变量和静态方法在有继承关系的两个类中的问题测试 ...
- Java中对象的深复制和浅复制详解
1.浅复制与深复制概念 ⑴浅复制(浅克隆) 被复制对象的所有变量都含有与原来的对象相同的值,而所有的对其他对象的引用仍然指向原来的对象.换言之,浅复制仅仅复制所考虑的对象,而不复制它所引用的对象. ⑵ ...
- java 中对象比较大小
java 中对象比较大小 java 中对象比较大小有两种方法 1:实现Comparable 接口 的 public int compareTo(T o) 方法: 2:实现Comparator 接口 的 ...
- Java中对象、对象引用、堆、栈、值传递以及引用传递的详解
Java中对象.对象引用.堆.栈.值传递以及引用传递的详解 1.对象和对象引用的差别: (1).对象: 万物皆对象.对象是类的实例. 在Java中new是用来在堆上创建对象用的. 一个对象能够被多个引 ...
- 谈谈java中成员变量与成员方法继承的问题
谈谈java中成员变量与成员方法继承的问题 关于成员变量和成员方法的的继承问题,我也可以做一个小测试,来看看结果. 首先我们先创建一个父类:
随机推荐
- ajax响应报文可以被浏览器缓存的必要条件
1.发送请求时必须使用get方法. 2.服务器端设置响应报文的Expires为希望浏览器缓存的时间 如果这两个条件都不满足时,也就是说无法通过浏览器缓存来缓存文件时: 在js中设置一个localCac ...
- 条件注释判断IE浏览器版本
lt,lte,gt,gte分别表示什么 lt:小于当前版本 lte:小于或等于当前版本,包括本身 gt:大于当前版本 gte:大于或等于当前版本,包括本身 使用格式 // 如IE9以下(不包括IE9加 ...
- CentOS配置bond
Bonding的模式一共有7种: #defineBOND_MODE_ROUNDROBIN 0 (balance-rr模式)网卡的负载均衡模式 #defineBOND_MODE_ACTI ...
- mac必备软件
LigthPaper:Markdown工具 ssh工具:ShellCraft
- mac必装工具以及mac使用介绍
必装工具 Scroll Reverserhttp://pilotmoon.com/scrollreverser/:一款可以使得鼠标使用方式和windows系统一致的软件 编程工具 ,,,,, 常用快捷 ...
- npm基础知识笔记
# NPM Study 1.npm组成 --网站 --命令行界面(CLI) --注册表 2.npm入门-创建属于你的npm账户 --https://www.npmjs.com/signup 5 ...
- pta 习题集 5-2 找出不是两个数组共有的元素 (5分)
给定两个整型数组,本题要求找出不是两者共有的元素. 输入格式: 输入分别在两行中给出两个整型数组,每行先给出正整数NN(≤20≤20),随后是NN个整数,其间以空格分隔. 输出格式: 在一行中按照数字 ...
- linux设备驱动开发详解 笔记
在目录的 Makefile 中关于 RTC_DRV_S3C 的编译脚本为: obj -$(CONFIG_RTC_DRV_S3C) += rtc-s3c.o 上述脚本意味着如果 RTC_DRV_S3 ...
- pymysql executemany
Cursor Objects — PyMySQL 0.7.2 documentation https://pymysql.readthedocs.io/en/latest/modules/cursor ...
- Unity-DLL反编译学习
本文由博主(SunboyL)原创,转载请注明出处:http://www.cnblogs.com/xsln/p/DLL_DeCompilation.html 在Unity实际开发过程中, ...
