Java 详解浅拷贝&&深拷贝
2016-04-16 14:29
603 查看
刚接触Java,今天看了java中对象的克隆,暂时就明白了这么多,记录下来,后面有更深的理解再追加,欢迎指正。
一、为什么需要克隆
在实际编程过程中,我们常常要遇到这种情况:有一个对象A,在某一时刻A中已经包含了一些有效值,此时可能会需要一个和A完全相同新对象B,并且此后对B任何改动都不会影响到A中的值,也就是说,A与B是两个独立的对象,但B的初始值是由A对象确定的。在Java语言中,用简单的赋值语句是不能满足这种需求的,要满足这种需求有很多途径。
二、克隆方式
1、浅拷贝
对于要克隆的对象,对于其基本数据类型的属性,复制一份给新产生的对象;
对于非基本数据类型的属性,仅仅复制一份引用给新产生的对象,即新产生的对象和原始对象中的非基本数据类型的属性都指向的是同一个对象。
做法:要克隆的类实现Cloneable接口
要克隆的类重载clone()方法
2、深拷贝
在浅度克隆的基础上,对于要克隆的对象中的非基本数据类型的属性对应的类,也实现克隆,这样对于非基本数据类型的属性,复制的不是一份引用,即新产生的对象和原始对象中的非基本数据类型的属性指向的不是同一个对象。、
做法:
要克隆的类和类中所有非基本数据类型的属性对应的类都实现Cloneable接口
要克隆的类和类中所有非基本数据类型的属性对应的类都重载clone()方法
若不拷贝对象,直接用引用,那么这两个访问的是同一个地址
那么我们在main()中调用时:
结果:
true
张三
张三
李四
李四
可以看出这并不是我们想要的结果,于是我们有了拷贝。
对于浅拷贝,我们直接上代码,看效果!!
在main函数中:
结果:
false
张三
张三
张三
李四
可以看出我们这样实现了克隆对象与原对象的分离。
好,接下来,我们想要给我们的product类加一个属性,该属性是类型是Attribute类
于是我们的Product类变成了这样:
Attribute类:
在main函数中,我们想克隆一份张三的属性帅哥给另一个李四的人
false
张三帅哥
张三帅哥
李四帅哥
张三帅哥
恩看样子我们达到了效果,但是现在我想给李四改属性了,给李四改成可爱类型的吧……
于是我们在main()函数中这样:
结果:
false
true
张三帅哥
张三帅哥
张三可爱
李四可爱
等等,这个结果不对啊,我只想给李四变成可爱,怎么张三也变可爱了呢?恩这里我们就引出了深拷贝,从main()中加的那一句,它们是同一个对象。
于是我们的Attribute类改成了这样:
现在我们在实验刚刚的呢?
在main()中这样调用:
结果:
false
false
张三帅哥
张三帅哥
张三帅哥
李四狗狗
现在我们的结果就是我们想要实现的了,也可以看出后面这种情况我们必须要用深拷贝才能达到我们的需求。
这里总结一下:
(1) 对于类只有基本数据类型的属性,只需在浅拷贝
1、 要克隆的类实现Cloneable接口
2、 要克隆的类重载clone()方法即可
(2) 对于类中有非基本数据类型的属性,需深拷贝
1、 要克隆的类和类中所有非基本数据类型的属性对应的类都实现Cloneable接口
2、 要克隆的类和类中所有非基本数据类型的属性对应的类都重载clone()方法
一、为什么需要克隆
在实际编程过程中,我们常常要遇到这种情况:有一个对象A,在某一时刻A中已经包含了一些有效值,此时可能会需要一个和A完全相同新对象B,并且此后对B任何改动都不会影响到A中的值,也就是说,A与B是两个独立的对象,但B的初始值是由A对象确定的。在Java语言中,用简单的赋值语句是不能满足这种需求的,要满足这种需求有很多途径。
二、克隆方式
1、浅拷贝
对于要克隆的对象,对于其基本数据类型的属性,复制一份给新产生的对象;
对于非基本数据类型的属性,仅仅复制一份引用给新产生的对象,即新产生的对象和原始对象中的非基本数据类型的属性都指向的是同一个对象。
做法:要克隆的类实现Cloneable接口
要克隆的类重载clone()方法
2、深拷贝
在浅度克隆的基础上,对于要克隆的对象中的非基本数据类型的属性对应的类,也实现克隆,这样对于非基本数据类型的属性,复制的不是一份引用,即新产生的对象和原始对象中的非基本数据类型的属性指向的不是同一个对象。、
做法:
要克隆的类和类中所有非基本数据类型的属性对应的类都实现Cloneable接口
要克隆的类和类中所有非基本数据类型的属性对应的类都重载clone()方法
若不拷贝对象,直接用引用,那么这两个访问的是同一个地址
public class Product{ private String name; Attribute attribute; public Product(String name) { this.name = name; } public Product(String name, String attribute) { this.name = name; this.attribute = new Attribute(attribute); } public String getName() { return name; } public void setName(String name) { this.name = name; } }
那么我们在main()中调用时:
Product p1 = new Product("张三"); Product p2 = p1; System.out.println(p1==p2); //是同一个对象 System.out.println(p1.getName()); System.out.println(p2.getName()); p2.setName("李四"); System.out.println(p1.getName()); System.out.println(p2.getName()); //访问的是同一个地址
结果:
true
张三
张三
李四
李四
可以看出这并不是我们想要的结果,于是我们有了拷贝。
对于浅拷贝,我们直接上代码,看效果!!
public class Product implements Cloneable { //注意实现Cloneable接口<span style="font-size:12px;"><span><span><span style="font-size:12px;"><span></span></span></span></span></span> private String name; public Product(String name) { this.name = name; } public Product(String name) { this.name = name; } public String getName() { return name; } public void setName(String name) { this.name = name; } public Object clone() { //重载clone()方法 try { return super.clone(); } catch (CloneNotSupportedException e) { return null; } } }
在main函数中:
Product p1 = new Product("张三"); Product p2 = (Product)p1.clone(); //在Product类中重载了clone方法 System.out.println(p1==p2); //对象不一样 System.out.println(p1.getName()); System.out.println(p2.getName()); p2.setName("李四"); System.out.println(p1.getName()); //对克隆对象的改变不会影响原对象 System.out.println(p2.getName()); // p1.setName("李四"); // System.out.println(p1.getName()); //对原对象的改变不会影响克隆对象 // System.out.println(p2.getName());
结果:
false
张三
张三
张三
李四
可以看出我们这样实现了克隆对象与原对象的分离。
好,接下来,我们想要给我们的product类加一个属性,该属性是类型是Attribute类
于是我们的Product类变成了这样:
public class Product implements Cloneable { private String name; Attribute attribute; //新增 public Product(String name) { this.name = name; } public Product(String name, String attribute) { this.name = name; this.attribute = new Attribute(attribute); // } public String getName() { return name; } public void setName(String name) { this.name = name; } public Object clone() { try { return super.clone(); } catch (CloneNotSupportedException e) { return null; } } }
Attribute类:
public class Attribute{ private String attribute; public String getAttribute() { return attribute; } public void setAttribute(String attribute) { this.attribute = attribute; } public Attribute(String attribute){ this.attribute = attribute; } }
在main函数中,我们想克隆一份张三的属性帅哥给另一个李四的人
Product p1 = new Product("张三","帅哥"); Product p2 = (Product)p1.clone(); //在Product类中重载了clone方法 System.out.println(p1==p2); //对象不一样 System.out.println(p1.getName()+p1.attribute.getAttribute()); System.out.println(p2.getName()+p2.attribute.getAttribute()); p2.setName("李四"); System.out.println(p2.getName()+p2.attribute.getAttribute());结果:
false
张三帅哥
张三帅哥
李四帅哥
张三帅哥
恩看样子我们达到了效果,但是现在我想给李四改属性了,给李四改成可爱类型的吧……
于是我们在main()函数中这样:
Product p1 = new Product("张三","帅哥"); Product p2 = (Product)p1.clone(); System.out.println(p1==p2); //对象不一样,clone() System.out.println(p1.attribute==p2.attribute); //注意这里,是true System.out.println(p1.getName()+p1.attribute.getAttribute()); System.out.println(p2.getName()+p2.attribute.getAttribute()); p2.setName("李四"); p2.attribute.setAttribute("可爱"); System.out.println(p1.getName()+p1.attribute.getAttribute()); //对克隆对象与 原对象的属性name之间无影响 //但是它们的属性attribute仍然指向相同的一个 System.out.println(p2.getName()+p2.attribute.getAttribute()); // p1.setName("李四"); // p1.attribute.setAttribute("可爱"); // System.out.println(p1.getName()+p1.attribute.getAttribute()); //对克隆对象与原对象的属性name之间无影响 // //但是它们得属性attribute仍然指向相同的一个 // System.out.println(p2.getName()+p2.attribute.getAttribute());
结果:
false
true
张三帅哥
张三帅哥
张三可爱
李四可爱
等等,这个结果不对啊,我只想给李四变成可爱,怎么张三也变可爱了呢?恩这里我们就引出了深拷贝,从main()中加的那一句,它们是同一个对象。
p1.attribute==p2.attribute我们想到,那就给模仿Product给Attribute类也重载clone()吧,也实现Cloneable接口。
于是我们的Attribute类改成了这样:
public class Attribute implements Cloneable { private String attribute; public String getAttribute() { return attribute; } public void setAttribute(String attribute) { this.attribute = attribute; } public Attribute(String attribute){ this.attribute = attribute; } public Object clone(){ try{ return super.clone(); }catch(CloneNotSupportedException e){ return null; } } }
现在我们在实验刚刚的呢?
在main()中这样调用:
Product p1 = new Product("张三","帅哥"); Product p2 = (Product)p1.clone(); //在Product类中重载了clone方法 p2.attribute = (Attribute)p1.attribute.clone(); //在Attribute中重载clone()方法 System.out.println(p1==p2); //对象不一样 System.out.println(p1.attribute==p2.attribute); System.out.println(p1.getName()+p1.attribute.getAttribute()); System.out.println(p2.getName()+p2.attribute.getAttribute()); p2.setName("李四"); p2.attribute.setAttribute("狗狗"); System.out.println(p1.getName()+p1.attribute.getAttribute()); //对克隆对象与原对象的属性name之间无影响,attribute也无影响 //但是他们得属性attribute System.out.println(p2.getName()+p2.attribute.getAttribute());
结果:
false
false
张三帅哥
张三帅哥
张三帅哥
李四狗狗
现在我们的结果就是我们想要实现的了,也可以看出后面这种情况我们必须要用深拷贝才能达到我们的需求。
这里总结一下:
(1) 对于类只有基本数据类型的属性,只需在浅拷贝
1、 要克隆的类实现Cloneable接口
2、 要克隆的类重载clone()方法即可
(2) 对于类中有非基本数据类型的属性,需深拷贝
1、 要克隆的类和类中所有非基本数据类型的属性对应的类都实现Cloneable接口
2、 要克隆的类和类中所有非基本数据类型的属性对应的类都重载clone()方法
相关文章推荐
- Eclipse卸载安装的插件
- java之servlet
- Sprng MVC异常处理总结
- JAVA基础学习一些总结
- DL4J (DeepLearning for java)
- JAVA初学,对面向对象的一点理解
- java Swing JTable 隐藏某列
- java基础(六),异常
- 【并发编程】java.lang.Thread.UncaughtExceptionHandler
- Spring web.xm spring-context spring-mvc
- Java中super的几种用法并与this的区别
- Spring AOP 学习例子
- Java Web 基础环境搭建
- Java集合(五):Set集
- spring annotation
- Java基础(容易忽略的java细节)
- Spring注解式与配置文件式
- Struts2之ModelDriven的使用
- struts2中的ModelDriven使用
- Struts2中的ModelDriven机制及其运用