您的位置:首页 > 编程语言 > Java开发

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()方法

若不拷贝对象,直接用引用,那么这两个访问的是同一个地址

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()方法
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: