Java的clone方法--深拷贝和浅拷贝
程序员文章站
2022-05-20 09:39:36
...
Cloneable接口是一个标记接口,也就是没有任何内容,定义如下:
clone方法是在Object种定义的,而且是protected型的,只有实现了这个接口,才可以在该类的实例上调用clone方法,否则会抛出CloneNotSupportException。Object中默认的实现是一个浅拷贝,也就是表面拷贝,如果需要实现深层次拷贝的话,必须对类中可变域生成新的实例。
Object提供了一个对象拷贝的默认方法clone方法,但是该方法是有缺陷的,它提供了一种浅拷贝方式,也就是它并不会把对象所有属性全部拷贝一份,而是有选择性的拷贝,拷贝规则如下:
1、基本类型
如果变量是基本类型,则拷贝其值,比如:int、float、long等。
2、String字符串
这个比较特殊,拷贝的是地址,是个引用,但是在修改的时候,它会从字符串池(String Pool)中重新生成新的字符串,原有的字符串对象保持不变,此处可以认为String是个基本类型。
3、对象
如果变量时一个实例对象,则拷贝地址引用,也就是说此时新拷贝出的对象与原有对象共享该实例变量,不受访问权限的限制。
总结一下:
浅层复制: 被复制的对象的所有成员属性都有与原来的对象相同的值,而所有的对其他对象的引用仍然指向原来的对象。换言之,浅层复制仅仅复制所考虑的对象,而不复制它所引用的对象。
深层复制:被复制对象的所有变量都含有与原来的对象相同的值,除去那些引用其他对象的变量。那些引用其他对象的变量将指向被复制过的新对象,而不是原有的那些被引用的对象。换言之,深层复制要复制的对象 所引用的对象都复制一遍。
举个例子:
public class Client {
public static void main(String[] args){
//定义父亲
Person p = new Person("父亲");
//定义大儿子
Person son1 = new Person("大儿子",p);
//定义小儿子
Person son2 = son1.clone();
//重新给son2起名
son2.setName("小儿子");
//给小儿子,找个干爹
son2.getFather().setName("干爹");
System.out.println(son1.getName()+" 的父亲是 "+son1.getFather().getName());
System.out.println(son2.getName()+" 的父亲是 "+son2.getFather().getName());
}
}
class Person implements Cloneable{
private String name;
private Person father;
public Person(String name){
this.name=name;
}
public Person(String name,Person father){
this.name=name;
this.father=father;
}
//拷贝的实现
@Override
public Person clone(){
Person p = null;
try{
//调用Object的浅拷贝
p = (Person) super.clone();
/**
* 1.重新覆写对象实例部分的拷贝,实现深拷贝
* 2.如果没有重写对象实例部分的拷贝,那么大儿子和小儿子的父亲会引用同一个父亲,
* 只要任意修改一下父亲,另外一个就会被修改
*/
p.setFather(new Person(p.getFather().getName()));
}catch(CloneNotSupportedException e){
e.printStackTrace();
}
return p;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Person getFather() {
return father;
}
public void setFather(Person father) {
this.father = father;
}
}
上一篇: Git入门第十二讲:分支管理