深拷贝和浅拷贝的区别,说法正确的是_前端浅拷贝和深拷贝的区别

深拷贝和浅拷贝的区别,说法正确的是_前端浅拷贝和深拷贝的区别首先,明确一点深拷贝和浅拷贝是针对对象属性为对象的,因为基本数据类型在进行赋值操作时(也就是拷贝)是直接将值赋给了新的变量,也就是该变量是原变量的一个副本,这个时候你修改两者中的任何一个的值都不会影响另一个,而对于对象或者引用数据来说在进行浅拷贝时,只是将对象的引用复制了一份,也就内存地址,即两个不同的变量指向了同一个内存地址,那么在改变任一个变量的值都是该变这个内存地址的所存储的值,所以两个变量的值都会改变。一、clone()方法在Java中是用clone()方法实现深拷贝的,比如以下代码在Jav

大家好,又见面了,我是你们的朋友全栈君。如果您正在找激活码,请点击查看最新教程,关注关注公众号 “全栈程序员社区” 获取激活教程,可能之前旧版本教程已经失效.最新Idea2022.1教程亲测有效,一键激活。

Jetbrains全系列IDE稳定放心使用

首先,明确一点深拷贝和浅拷贝是针对对象属性为对象的,因为基本数据类型在进行赋值操作时(也就是拷贝)是直接将值赋给了新的变量,也就是该变量是原变量的一个副本,这个时候你修改两者中的任何一个的值都不会影响另一个,而对于对象或者引用数据来说在进行浅拷贝时,只是将对象的引用复制了一份,也就内存地址,即两个不同的变量指向了同一个内存地址,那么在改变任一个变量的值都是该变这个内存地址的所存储的值,所以两个变量的值都会改变。

一、clone()方法

在Java中是用clone()方法实现深拷贝的,比如以下代码在Java中是很常见的

Person p = new Person(23, "zhang");
		Person p1 = p;
		
		System.out.println(p);
		System.out.println(p1);

这段代码的打印结果为:

testclone.Person@2f9ee1ac
testclone.Person@2f9ee1ac 

可以看出两者打印的地址是相同的,既然地址相同那就代表两者是同一个对象,而p和p1都只是引用罢了。代码执行后,内存中的情况

深拷贝和浅拷贝的区别,说法正确的是_前端浅拷贝和深拷贝的区别 

而下面这段代码才是真的克隆了一个对象

Person p = new Person(23, "zhang");
		Person p1 = (Person) p.clone();
		
		System.out.println(p);
		System.out.println(p1)

打印的结果为:

testclone.Person@2f9ee1ac
testclone.Person@67f1fba0

从结果来看两者不是同一个地址,那自然就不是同一个对象,而内存情况如下

深拷贝和浅拷贝的区别,说法正确的是_前端浅拷贝和深拷贝的区别 二、深拷贝和浅拷贝

在上面的代码中Person中有两个成员变量,分别是name和age, name是String类型, age是int类型。代码非常简单,如下所示:

public class Person implements Cloneable{
	
	private int age ;
	private String name;
	
	public Person(int age, String name) {
		this.age = age;
		this.name = name;
	}
	
	public Person() {}
 
	public int getAge() {
		return age;
	}
 
	public String getName() {
		return name;
	}
	
	@Override
	protected Object clone() throws CloneNotSupportedException {
		return (Person)super.clone();
	}
}

 Person类的两个成员变量中,age是基本数据类型,关于基本数据类型的拷贝在上面就已经说过了,但是name是引用数据类型,它只是一个引用, 指向一个真正的String对象,那么对它的拷贝有两种方式: 直接将源对象中的name的引用值拷贝给新对象的name字段, 或者是根据原Person对象中的name指向的字符串对象创建一个新的相同的字符串对象,将这个新字符串对象的引用赋给新拷贝的Person对象的name字段。这两种拷贝方式分别叫做浅拷贝和深拷贝。深拷贝和浅拷贝的原理如下图所示:
深拷贝和浅拷贝的区别,说法正确的是_前端浅拷贝和深拷贝的区别
深拷贝和浅拷贝的区别,说法正确的是_前端浅拷贝和深拷贝的区别

下面我们通过代码来验证:如果两个Person对象的name的地址值相同, 说明两个对象的name都指向同一个String对象, 也就是浅拷贝, 而如果两个对象的name的地址值不同, 那么就说明指向不同的String对象, 也就是在拷贝Person对象的时候, 同时拷贝了name引用的String对象, 也就是深拷贝。验证代码如下:

Person p = new Person(23, "zhang");
		Person p1 = (Person) p.clone();
		
		String result = p.getName() == p1.getName() 
				? "clone是浅拷贝的" : "clone是深拷贝的";
		
		System.out.println(result);

打印的结果为:

clone是浅拷贝

也就是clone方法本身是一个浅拷贝的方法,而上一个代码我们又得出clone是深拷贝,那么是我前面写错了吗?那当然不是,clone方法本身是浅拷贝的,但是我们可以通过重写该方法来实现深拷贝 ,所以要实现深拷贝我们就需要实现Cloneable接口,覆盖并实现clone方法,除了调用父类中的clone方法得到新的对象, 还要将该类中的引用变量也clone出来。如果只是用Object中默认的clone方法,是浅拷贝的,再次以下面的代码验证:

static class Body implements Cloneable{
		public Head head;
		
		public Body() {}
 
		public Body(Head head) {this.head = head;}
 
		@Override
		protected Object clone() throws CloneNotSupportedException {
			return super.clone();
		}
		
	}
	static class Head /*implements Cloneable*/{
		public  Face face;
		
		public Head() {}
		public Head(Face face){this.face = face;}
		
	} 
	public static void main(String[] args) throws CloneNotSupportedException {
		
		Body body = new Body(new Head());
		
		Body body1 = (Body) body.clone();
		
		System.out.println("body == body1 : " + (body == body1) );
		
		System.out.println("body.head == body1.head : " +  (body.head == body1.head));
		
		
	}

 在上述代码中有两个主要的类,Body和Face,Body类中又组合了Face对象,在对Body进行clone时,它所组合的Face对象时浅拷贝,其打印结果可以很好的证明这一点:

body == body1 : false
body.head == body1.head : true

如果要使Body对象在clone时进行深拷贝, 那么就要在Body的clone方法中,将源对象引用的Head对象也clone一份。

static class Body implements Cloneable{
		public Head head;
		public Body() {}
		public Body(Head head) {this.head = head;}
 
		@Override
		protected Object clone() throws CloneNotSupportedException {
			Body newBody =  (Body) super.clone();
			newBody.head = (Head) head.clone();
			return newBody;
		}
		
	}
	static class Head implements Cloneable{
		public  Face face;
		
		public Head() {}
		public Head(Face face){this.face = face;}
		@Override
		protected Object clone() throws CloneNotSupportedException {
			return super.clone();
		}
	} 
	public static void main(String[] args) throws CloneNotSupportedException {
		
		Body body = new Body(new Head());
		
		Body body1 = (Body) body.clone();
		
		System.out.println("body == body1 : " + (body == body1) );
		
		System.out.println("body.head == body1.head : " +  (body.head == body1.head));
		
		
	}

 

打印结果为:
body == body1 : false
body.head == body1.head : false

由此可见, body和body1内的head引用指向了不同的Head对象, 也就是说在clone Body对象的同时, 也拷贝了它所引用的Head对象, 进行了深拷贝。
从以上的内容中我们可以知道,一个对象要实现深拷贝,就要实现Cloneable接口,重写clone()方法,并在clone()方法中将对象引用的其他对象也要clone一份,这就要求被引用的对象也要实现Cloneable接口,并实现clone()方法。

而按照这个结论,Body类中组合了Head类,而Head类中又组合了Face类,在对Body对象进行深拷贝时,会拷贝其中的Head类,而这时默认执行的时浅拷贝,也就是说其中的Face对象并不会被拷贝,代码如下:

static class Body implements Cloneable{
		public Head head;
		public Body() {}
		public Body(Head head) {this.head = head;}
 
		@Override
		protected Object clone() throws CloneNotSupportedException {
			Body newBody =  (Body) super.clone();
			newBody.head = (Head) head.clone();
			return newBody;
		}
		
	}
	
	static class Head implements Cloneable{
		public  Face face;
		
		public Head() {}
		public Head(Face face){this.face = face;}
		@Override
		protected Object clone() throws CloneNotSupportedException {
			return super.clone();
		}
	} 
	
	static class Face{}
	
	public static void main(String[] args) throws CloneNotSupportedException {
		
		Body body = new Body(new Head(new Face()));
		
		Body body1 = (Body) body.clone();
		
		System.out.println("body == body1 : " + (body == body1) );
		
		System.out.println("body.head == body1.head : " +  (body.head == body1.head));
		
		System.out.println("body.head.face == body1.head.face : " +  (body.head.face == body1.head.face));
		
		
	}

打印结果为:

body == body1 : false
body.head == body1.head : false
body.head.face == body1.head.face : true

 深拷贝和浅拷贝的区别,说法正确的是_前端浅拷贝和深拷贝的区别

那我们可以想一想,这对Body对象来说,到底是不是深拷贝,其实是可以算的,因为Body对象中的引用对象是拷贝了的(这段代码中只有Head对象的引用),也就是说两个独立的Body对象中的Head引用指向了两个独立的Head对象,但对于Head对象来说,其Face引用指向了同一个Face对象。所以严格来说这是一种不彻底的深拷贝,而若是想要彻底的深拷贝,就要保证该对象的所有引用对象的类型都要去实现Cloneable接口,实现clone方法。 

参考文献:https://blog.csdn.net/zhangjg_blog/article/details/18369201

 

 

 

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

发布者:全栈程序员-用户IM,转载请注明出处:https://javaforall.cn/187265.html原文链接:https://javaforall.cn

【正版授权,激活自己账号】: Jetbrains全家桶Ide使用,1年售后保障,每天仅需1毛

【官方授权 正版激活】: 官方授权 正版激活 支持Jetbrains家族下所有IDE 使用个人JB账号...

(0)
blank

相关推荐

  • uIP介绍[通俗易懂]

    uIP介绍[通俗易懂]下面内容都是参考英文文档uip是一个开源的微型协议栈,主要用于8位,16位MCU,占用内存少,并且代码少,容易移植。它既可以用于多任务的操作系统中,如ucos。也能单独存在,传说中的裸奔。uip的主循环uip主循环中重复做着两件事情。查看是否收到数据包查看周期性超时是否发生如果有数据包到达,则会在主循环中调用输入处理函数,uip_input(),

  • 动态规划之01背包问题及其优化(python实现)「建议收藏」

    动态规划之01背包问题及其优化(python实现)「建议收藏」动态规划之01背包问题及其优化(python实现)**背包问题(**Knapsackproblem)是一种组合优化的NP完全问题。问题描述为:给定一组物品,每种物品都有自己的重量和价格,在限定的总重量内,我们如何选择,才能使得物品的总价格最高。问题的名称来源于如何选择最合适的物品放置于给定背包中。解决思路:动态规划,对每一件物品遍历背包容量,当背包可容纳值大于等于当前物品,与之前已放…

  • DropDownList1对数据库的操作「建议收藏」

    DropDownList1对数据库的操作「建议收藏」面试的时候一个DropDownList1控件对数据库的增加难住了,翻翻以前做过的项目,原来如此的简单,晒出来分享一下。    1.添加数据 //得到数据datasetpublicvoidbind(stringsql)       {           stringst=@”DataSource=PC-20140331BMRR\SQLEXPRESS

  • linux指令_linux最常用命令

    linux指令_linux最常用命令基本命令关机:shutdown-hhaltinit0poweroff重启:shutdown-rrebootinit6pwd:查看工作目录ls:查看指定目录的内容-l:列表显示-a:显示所有,包括隐藏文件-h:人性化的显示-d:只显示目录,不查看内容cd:切换工作目录.:当前目录..:上一级目录~:用户家目录-:上次切过来的目录目录结构:linux目录…

  • 北京航空航天博士生的待遇_北航博士补贴涨到3500

    北京航空航天博士生的待遇_北航博士补贴涨到3500第二十四条博士后在站期间的工资及其他相关待遇,按照国家有关规定执行。博士后在站期满,工资停发,住房公积金及住房补贴随之停发。第二十五条企业博士后的工资、住房等福利待遇由与我校签订协议的联合培养单位负责。在职博士后的工资、住房等待遇由原所在单位或合作导师负责。第二十六条博士后在站期间,学校为非在职博士后建立住房公积金,并为暂时没有租赁到公寓的非在职博士后发放住房补贴1000元/月。博士后公寓的…

    2022年10月29日
  • centos7 输入 ifconfig 不显示 ip 地址 连接不上的解决方法(亲测成功)「建议收藏」

    centos7 输入 ifconfig 不显示 ip 地址 连接不上的解决方法(亲测成功)「建议收藏」最近又把自己的虚拟机打开了玩玩集群,遇到一个小问题,我发现虚拟机的内存不够了,就把虚拟机关机加大了内存,谁知道开机后,ifconfig或者ipaddr显示没有ip地址,只显示一个lo,没有ens33,没有ip地址就没法用xshell连接,很蛋疼,网上也有很多解决方案,但都写的乱七八糟的,而且很多都不好使,今天就来介绍一下我最后解决的方法.我说一下我的虚拟机的情况,我三台虚拟机,之前是mas………

    2022年10月23日

发表回复

您的电子邮箱地址不会被公开。

关注全栈程序员社区公众号