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

java 设计模式学习笔记(7) - 浅拷贝和深拷贝

2011-04-16 21:28 896 查看
在JAVA 语言中,拷贝按拷贝结果分为两种:浅拷贝,深拷贝

浅拷贝 :“被克隆对象的所有变量都含有与原来的对象相同的值,而它所有的对其他对象的引用都仍然指向原来的对象。换一种说法就是浅克隆仅仅克隆所考虑的对象,而不克隆它所引用的对象。”

深拷贝: “被克隆对象的所有变量都含有与原来的对象相同的值,但它所有的对其他对象的引用不再是原有的,而这是指向被复制过的新对象。换言之,深复制把要复制的对象的所有引用的对象都复制了一遍,这种叫做间接复制。”

quote :http://sakyone.iteye.com/blog/484099

我对浅拷贝的理解: 开辟了另外一部分和原拷贝对象同样大小的内存空间,该内存空间所有内容和原拷贝对象在内存空间的内容一摸一样,对其他对象的引用也都指向同样的对象。

一个Teacher 对象,保存了对一个 Student 的引用。 浅拷贝后,建立了一个Teacher 对象,并且其对Student 的引用指向了同一个 Studuent 对象。

我对深拷贝的理解: 在内存中开辟了另外一部分和原拷贝对象同样大小的内存空间,该内存空间所有内容和原拷贝对象在内存空间的内容一摸一样,同时在内存中还开辟了一些内存空间来保存拷贝对象引用的其他对象。复制出的对象中的引用指向了复制出的其他对象。

一个Teacher 对象,保存了对一个 Student 的引用。 深拷贝后,建立了一个Teacher 对象,还建立了一个Student 对象,并是复制出来的Teacher 中的Student 引用指向这个新建立的Student 。

在将深拷贝和浅拷贝的是如何实现之前,先来简单介绍下 equals 和 == 的区别:

equals 作为 object 的比较方法,对于任何非空引用值
x
y
,当且仅当
x
y
引用同一个对象时,此方法才返回
true。


在比较String 类型时: <JDK > 当且仅当该参数不为
null
,并且是与此对象表示相同字符序列的
String
对象时,结果才为
true


Integer(int) 无 equals 方法

== 在比较String 类型时,比较的是两个String 在内存中的首地址。

String str = "shallCopyStr";
String str2 = str;
System.out.println(str == str2);
System.out.println(str.equals(str2));

String str3 = new String(str2);
System.out.println(str == (str3) );
System.out.println(str.equals(str3));

String str4 = new String("shallCopyStr");
System.out.println(str == (str4) );
System.out.println(str.equals(str4));

System.out.println(str3 == (str4) );
System.out.println(str3.equals(str4));

true
true
false
true
false
true
false
true


可以看到 str != str3;

str != str4;

因为String 是经常被用到的类型,在JVM 中存在着一个 String 池,保存着很多String 对象用来共享使用。

String str = "shallCopyStr" 则先使用 equals() 方法来检查 String 池中是否存在 "shallCopyStr" 的String,如果存在,则将 str 指向该String对象,如果不存在,则在String 池中新建一个 内容为"shallCopyStr" 的String对象,并将str 指向新建的这个String 对象。String str3 =new String(str2) 语句执行的后果是在堆内存(堆内存专门用户保存 new 的对象和数组 )中保存一个 String("shallCopyStr") 的对象,并将引用返回给str3 ,也就是说str3 实际上指向的是堆内存的 对象。str 和 str3 一个是指向 String 池,一个是指向 堆内存,当然 用 == 来判断内存地址是不相同的。同样,str3 和str4 因为分别指向堆内存中的两个不同的内存,其指向的内存地址当然不相同,所以用 == 来判断也是不相同的。

对于非String 的类,== 和equals 比较的都是对象在堆内存的首地址,而对于基本类型的比较只能用 == ,而没有equal 方法,不能编译通过。

浅拷贝的和深拷贝是怎么实现的?

String str = "shallCopy" ;
String str2 = new String(str);
String str3 = str;

System.out.println(str == str2);  //false
System.out.println(str == str3); //true


浅拷贝实现方式: 给对象直接赋值

深拷贝实现方式之一 : 使用构造函数

String str2 = new String(str);

str2 ! =str 说明两个String 对象在堆内存中的首地址不一样,当然这在前面已经讨论过了。

现在以对象来说明问题:新建了一个TESTShallCopy 类,有两个构造函数。

public class TESTShallCopy {
private String name ;
private TESTShallCopy child;
public TESTShallCopy(String name){
this.name = name;
}

public TESTShallCopy(TESTShallCopy child){
this.child = child;
}

}


TESTShallCopy test = new TESTShallCopy("test");
TESTShallCopy test2 = test;
TESTShallCopy test3 = new TESTShallCopy(test);
System.out.println(test == test2);  //true
System.out.println(test == test3);   //false;


这个实验证明了 直接赋值后,两个对象引用都指向同一个对象,是浅拷贝。而使用构造函数来新建一个对象,那么两个对象的引用就没有指向同一个对象,也就是两个对象指向的堆内存首地址是不一样的,是深拷贝。

深拷贝实现方式之二 : 使用clone() 方法

现在改写 TESTShallCopy 类,让其实现Cloneable 接口,然后重写 Clone()方法

public class TESTShallCopy implements Cloneable{
private String name ;
private TESTShallCopy child;

public TESTShallCopy(String name){
this.name = name;
}

public TESTShallCopy(TESTShallCopy child){
this.child = child;
}

public Object clone(){
TESTShallCopy t = null;
try{
t = (TESTShallCopy)super.clone();
}catch(CloneNotSupportedException e){
e.printStackTrace();
}
return t;
}
}


现在用实验来验证 Clone() 方法是否能实现 深拷贝。

String[] ss={"111","222","333"};
String[] ww=(String[])ss.clone();
System.out.println(ss == ww);   //false

int[]  aa={1,2,3,4,5,6,7};
int[] cc = (int[]) aa.clone();
System.out.println(aa == cc);   //false

TESTShallCopy test = new TESTShallCopy("test");
TESTShallCopy test2 =(TESTShallCopy) test.clone();
System.out.println(test == test2);   //false


果然,使用clone 方法来复制得到的对象和原对象都 != ,因此可以知道 使用 clone() 方法也能实现深拷贝
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: