在完成实验时,明明返回的是一个clone以后的对象,修改这个对象却会把原对象的值也修改了,所以在这里记录以下这个容易犯的错误。
一.引用拷贝
这是最常见的拷贝,只要用“=”就可以完成,也是最简单的拷贝
String a = "Hello World!";
String b = a;
通过debug可以观察到两者指向的是相同的内存单元:
用图来解释就是这样:
二.对象拷贝
1.浅层拷贝
被复制对象的所有变量都含有与原来的对象相同的值,而所有的对其他对象的引用仍然指向原来的对象。简单来说,就是只拷贝第一层的对象,而对象的里的引用则不进行拷贝。例如以下代码:
public class main {
public static void main(String[] args) throws CloneNotSupportedException {
MyString a = new MyString("Hello World!");
MyString b = (MyString) a.clone();
System.out.println(a);
System.out.println(b);
}
}
// 注意这里需要实现Cloneable接口
class MyString implements Cloneable{
String l;
public MyString(String a) {
this.l = a;
}
@Override
public Object clone() throws CloneNotSupportedException{
return super.clone();
}
}
输出结果为
而debug进去能够发现:
指向的都为同一个字符串Hello World存储的内存区域
用图示来理解则是
2.深层拷贝
深拷贝是一个整个独立的对象拷贝,深拷贝会拷贝所有的属性,并拷贝属性指向的动态分配的内存。当对象和它所引用的对象一起拷贝时即发生深拷贝。深拷贝相比于浅拷贝速度较慢并且花销较大。简而言之,深拷贝把要复制的对象所引用的对象都复制了一遍。
在实现深拷贝时,可以考虑递归得对每一个对象的属性里引用的对象都clone,但是这样当类与类之间的关系比较复杂或属性比较多时,很难操作,故这里使用实现Serializable接口的方法
import java.io.*;
public class main {
public static void main(String[] args) throws Exception {
MyString a = new MyString("Hello World!");
MyString b = (MyString) a.deepClone();
System.out.println(a);
System.out.println(b);
}
}
//注意这里需要实现Serializable接口
class MyString implements Serializable {
String l;
public MyString(String a) {
this.l = a;
}
// 深度拷贝
public Object deepClone() throws Exception{
// 序列化
ByteArrayOutputStream bos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(bos);
oos.writeObject(this);
// 反序列化
ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray());
ObjectInputStream ois = new ObjectInputStream(bis);
return ois.readObject();
}
}
代码输出
进入debug进行观察可以发现两个l指向不同的内存空间
用图来理解则是: