目录
Object.clone()方法
如何使用
浅拷贝
深拷贝
如何实现深拷贝
Object.clone()方法
clone()是Object类中的方法可以拷贝出一个和调用者完全相同的方法,在Object类中,定义如下:
//拥有native关键字,说明是让底层C++来帮我们实现的,需要通知操作系统协助我们,也就是系统调用。
//使用了protected修饰,子类无法继承
protected native Object clone() throws CloneNotSupportedException;
如何使用
想要使用clone方法,必须自己主动重写,并且,重写的类必须实现Cloneable接口,例如
class A implements Cloneable {
int a;
String name;
B bb;
String[] arrs ;
public A(){}
public A( int a, String name ){
this.a = a;
this.name = name;
arrs = new String[2];
arrs[0] = "金轮";
arrs[1] = "起飞";
}
//主动重写clone方法,由于使用了系统调用,我们无法主动设定内容,只能调用Object的clone()
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
@Override
public String toString() {
return "A{" +
"a=" + a +
", name='" + name + '\'' +
", bb=" + bb +
", arrs=" + Arrays.toString(arrs) +
'}';
}
}
用以下代码作为验证克隆结果
A a = new A(120 , "芜湖大司马");
B bb = new B("芜湖飞机场");
a.bb = bb;
A b = (A)a.clone();
System.out.println(a);
System.out.println(b);
System.out.println(a == b);
//控制台输出
A{a=120, name='芜湖大司马', bb=B@7f31245a, arrs=[金轮, 起飞]}
A{a=120, name='芜湖大司马', bb=B@7f31245a, arrs=[金轮, 起飞]}
false
//B类
class B {
String add;
public B( String add ){
this.add = add;
}
}
可以看到,确实复制了属性一模一样的对象,但是地址并不相同。它们的内存空间是不一样的。
但是仔细观察!
//哎?这两个对象中的这个B类对象为什么是同一个?
bb=B@7f31245a
bb=B@7f31245a
浅拷贝
浅拷贝就是,通过clone方法,开辟一块新的内存空间,将原对象的基本数据类型和字符串(int,double,String...)直接拷贝一份新的赋值给新对象。将引用数据类型直接指向原对象使用的对象。
结构图如下:
直接重写Object的clone()方法,只能实现浅拷贝。
//只能实现浅拷贝
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
测试一下
A a = new A(120 , "芜湖大司马");
B bb = new B("芜湖飞机场");
a.bb = bb;
A b = (A)a.clone();
System.out.println(a);
System.out.println(b);
System.out.println(a == b);
//修改拷贝对象的String
b.name = "芜湖";
//直接重新new一个B给拷贝对象
b.bb = new B("芜湖停车场");
//修改一下拷贝对象的arrs[1]
b.arrs[1] = "田赋";
System.out.println(a);
System.out.println(b);
//控制台打印
A{a=120, name='芜湖大司马', bb=B@7f31245a, arrs=[金轮, 起飞]}
A{a=120, name='芜湖大司马', bb=B@7f31245a, arrs=[金轮, 起飞]}
false
A{a=120, name='芜湖大司马', bb=B@7f31245a, arrs=[金轮, 田赋]}
//name不影响,bb的指向被修改,arrs由于是浅拷贝,两者使用的是同一个对象,所以修改拷贝对象会影响原对象
A{a=120, name='芜湖', bb=B@6d6f6e28, arrs=[金轮, 田赋]}
//A类
class A implements Cloneable {
int a;
String name;
B bb;
String[] arrs ;
public A(){}
public A( int a, String name ){
this.a = a;
this.name = name;
arrs = new String[2];
arrs[0] = "金轮";
arrs[1] = "起飞";
}
//主动重写clone方法,由于使用了系统调用,我们无法主动设定内容,只能调用Object的clone()
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
@Override
public String toString() {
return "A{" +
"a=" + a +
", name='" + name + '\'' +
", bb=" + bb +
", arrs=" + Arrays.toString(arrs) +
'}';
}
}
class B {
String add;
public B( String add ){
this.add = add;
}
}
由上述代码可以看出,确实是浅拷贝。
深拷贝
深拷贝指的是,在通过clone()方法拷贝对象时,无论是基本数据类型还是引用数据类型,都会复制出一个新的对象来,生成两个从头到尾都不相同的对象
如何实现深拷贝
让所有引用数据类型的类都实现clone()方法是实现不了的。
需要在A类中拓展clone方法。
核心观念:对引用数据类型进行生成新对象并改变引用,也就是手动拷贝引用数据类型
//在A的clone方法中不直接返回super.clone的结果
//返回前对其中的引用数据类型进行new新对象或者也进行clone
//由于引用数据类型是自己实现的,所以可以这么写
@Override
protected Object clone() throws CloneNotSupportedException {
A na = (A) super.clone();
na.bb = new B(bb.add);
na.arrs = Arrays.copyOf(na.arrs,na.arrs.length);
return na;
}
//或者
//套娃,让其中的引用数据类型也重新赋值为clone();
@Override
protected Object clone() throws CloneNotSupportedException {
A na = (A) super.clone();
na.bb = na.bb.clone();
na.arrs = Arrays.copyOf(na.arrs,na.arrs.length);
return na;
}
这样修改之后,再次运行代码,控制台打印就变了
b.name = "芜湖";
b.bb.add = "芜湖停车场";
b.arrs[1] = "田赋";
System.out.println(a);
System.out.println(b);
//可见修改值并不会影响原数组了,拷贝成功
A{a=120, name='芜湖大司马', bb=B{add='芜湖飞机场'}, arrs=[金轮, 起飞]}
A{a=120, name='芜湖', bb=B{add='芜湖停车场'}, arrs=[金轮, 田赋]}