谈谈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中成员变量与成员方法继承的问题 关于成员变量和成员方法的的继承问题,我也可以做一个小测试,来看看结果. 首先我们先创建一个父类:
随机推荐
- WEB安全番外第一篇--其他所谓的“非主流”漏洞:URL跳转漏洞与参数污染
一.URL跳转篇: 1.原理:先来看这段代码: <?php if(isset($_GET["url_redircetion_target"])){ $url_redirect ...
- 【office2010】office2010安装问题的解决方案。
今天想在公司电脑上按上一个office2010,结果出现一个问题,导致研究了一下午才解决:现总结解决方案: 安装office 2010,提示需要安装MSXML版本6.10.1129.0组件.但是在网上 ...
- C++程序风格的思考
转载自:http://www.cppblog.com/weiym/archive/2013/04/27/199781.html 发现厚积薄发中有很多值得学习的东西 故引用之: 最近有机会看号称是公司最 ...
- asp.net SessionState模式的配置及使用
由于项目dll文件变动比较频繁,而保存登陆的状态又保存在Session中,所以导致用户经常无故掉线(PS:dll变动的时候导致Session).有一种方法可以长期保存session,那就是sessio ...
- oracle之用户名密码包含特殊字符时候怎么使用sqlplus登录
oracle有时候用户密码包含一些特殊字符直接登录会报错,需要使用以下方式登录sqlplus sqlplus 'username/"password"' PS:整体使用单引号括起来 ...
- nginx repos
vim /etc/yum.repos.d/nginx.repo 然后将下面的内容复制进去: [nginx] name=nginx repo baseurl=http://nginx.org/packa ...
- scrapy爬虫系列之五--CrawlSpider的使用
功能点:CrawlSpider的基本使用 爬取网站:保监会 主要代码: cf.py # -*- coding: utf-8 -*- import scrapy from scrapy.linkextr ...
- 持续交付的Mesos与Docker导入篇
变革这个词在当今的数字化时代司空见惯,IT技术每过一段时间就会有一起革新,从WEB2.0.虚拟化.云计算.大数据.微架构.DevOps再到今天的容器Docker与Mesos. Docker的出现方便了 ...
- 洛谷P1156 垃圾陷阱 dp
正解:dp 解题报告: 这儿是传送门! 话说最近怎么神仙们都开始狂刷dp,,,感觉今天写了好多dp的题解的样子?(也就三四道其实× 然后这题,首先看到要么吃要么堆起来就会想到01背包趴?然后就考虑设方 ...
- 使用celery之怎么让celery跑起来(转)
原文:http://www.dongwm.com/archives/how-to-use-celery/ 前言 自从发了上次的文章使用celery之深入celery配置, 有一些网友再问我怎么让cel ...