1700441932
1700441933
上面仅仅修改了加粗字体部分,大儿子重新设置了父亲名称,我们期望的输出是:将大儿子父亲的名称修改为干爹,小儿子的父亲名称保持不变。下面来检查一下结果是否如此:
1700441934
1700441935
大儿子的父亲是干爹
1700441936
1700441937
小儿子的父亲是干爹
1700441938
1700441939
怎么回事,小儿子的父亲也成了“干爹”?两个儿子都没有,岂不是要气死“父亲”了!出现这个问题的原因就在于clone方法,我们知道所有类都继承自Object, Object提供了一个对象拷贝的默认方法,即上面代码中的super.clone方法,但是该方法是有缺陷的,它提供的是一种浅拷贝方式,也就是说它并不会把对象的所有属性全部拷贝一份,而是有选择性的拷贝,它的拷贝规则如下:
1700441940
1700441941
(1)基本类型
1700441942
1700441943
如果变量是基本类型,则拷贝其值,比如int、float等。
1700441944
1700441945
(2)对象
1700441946
1700441947
如果变量是一个实例对象,则拷贝地址引用,也就是说此时新拷贝出的对象与原有对象共享该实例变量,不受访问权限的限制。这在Java中是很疯狂的,因为它突破了访问权限的定义:一个private修饰的变量,竟然可以被两个不同的实例对象访问,这让Java的访问权限体系情何以堪!
1700441948
1700441949
(3)String字符串
1700441950
1700441951
这个比较特殊,拷贝的也是一个地址,是个引用,但是在修改时,它会从字符串池(String Pool)中重新生成新的字符串,原有的字符串对象保持不变,在此处我们可以认为String是一个基本类型。(有关字符串的知识详见第4章。)
1700441952
1700441953
明白了这三个规则,上面的例子就很清晰了,小儿子对象是通过拷贝大儿子产生的,其父亲都是同一个人,也就是同一个对象,大儿子修改了父亲名称,小儿子也就跟着修改了—于是,父亲的两个儿子都没了!其实要更正也很简单,clone方法的代码如下:
1700441954
1700441955
public Person clone(){
1700441956
1700441957
Person p=null;
1700441958
1700441959
try{
1700441960
1700441961
p=(Person)super.clone();
1700441962
1700441963
p.setFather(new Person(p.getFather().getName()));
1700441964
1700441965
}catch(CloneNotSupportedException e){
1700441966
1700441967
e.printStackTrace();
1700441968
1700441969
}
1700441970
1700441971
return p;
1700441972
1700441973
}
1700441974
1700441975
然后再运行,小儿子的父亲就不会是“干爹”了。如此就实现了对象的深拷贝(Deep Clone),保证拷贝出来的对象自成一体,不受“母体”的影响,和new生成的对象没有任何区别。
1700441976
1700441977
注意 浅拷贝只是Java提供的一种简单拷贝机制,不便于直接使用。
1700441978
1700441979
1700441980
1700441981
[
上一页 ]
[ :1.700441932e+09 ]
[
下一页 ]