淺拷貝和深拷貝的區別
淺拷貝和深拷貝都是複製對象,複製出來的對象,它們的內存地址都是重新分配的,區別在於淺拷貝對象中的引用類型和原對象中的引用類型指向同一個內存地址,而深拷貝對象中的引用類型的內存地址是重新分配的,也就是說,淺拷貝對象和原對象的引用類型的數據是同步的,深拷貝對象和原對象的引用類型的數據是互不干擾的。
注意: 這裏說的是引用類型!對於對象中直接定義的基本數據類型及其包裝類型、String
類型這些數據類型,由於原對象和拷貝對象的內存地址是重新分配的,因此這些數據的改變不會影響到另一個對象。
注意: 再強調一次,請區分對象中的基本數據類型及其包裝類型、String
類型,這些不屬於引用類型!特別要注意的是,淺拷貝中,對象中的引用類型的地址是同一個內存地址,引用類型中的基本數據類型及其包裝類型、String
類型的改變會同步到所有淺拷貝對象及原對象中!
淺拷貝的實現
- 方法一:類要實現
Cloneable
接口,重寫Object
的clone
方法,在clone
方法中調用super.clone()
方法即可。 - 方法二:只要能實現一個對象的所有屬性都拷貝到另一個新對象的屬性中即可,通常使用方法一
public class ShallowCopy implements Cloneable {
public ShallowCopy() {
}
public ShallowCopy(int age, Integer count, String name, Bean bean) {
this.age = age;
this.count = count;
this.name = name;
this.bean = bean;
}
private int age;
private Integer count;
private String name;
private Bean bean;
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
@Override
public String toString() {
return "ShallowCopy hash is " + this.hashCode() + " ,{" + "name=" + name + ", count=" + count + ", age='" + age + '\'' + ", bean=" + bean + "}";
}
}
深拷貝的實現
深拷貝有兩種實現方式
- 方法一:所有自定義類都要實現
Cloneable
接口,重寫Object
的clone
方法,在對象的clone
方法中先調用父類的clone
方法生成當前類的新對象,然後調用各個引用類型的clone
方法生成各個引用類型的新對象,最後把所有引用類型的對象設置到當前類的新對象中。 - 方法二:通過對象序列化實現深拷貝。所有引用類型都要實現
Serializable
接口,各個屬性要有setter
和getter
方法,通過ByteArrayOutputStream、ObjectOutputStream、ByteArrayInputStream和ObjectInputStream
完成對象的拷貝。
public class DeepCopy implements Serializable {
public DeepCopy() {
}
public DeepCopy(int age, Integer count, String name, Bean bean) {
this.age = age;
this.count = count;
this.name = name;
this.bean = bean;
}
private int age;
private Integer count;
private String name;
private Bean bean;
@Override
public String toString() {
return "DeepCopy hash is " + this.hashCode() + " ,{" + "age=" + age + ", count=" + count + ", name='" + name + '\'' + ", bean=" + bean + '}';
}
}
上述代碼涉及到的類,所有類的setter和getter都省略了
public class Bean implements Serializable {
public Bean() {
}
public Bean(String clazz, int x, Integer y) {
this.clazz = clazz;
this.x = x;
this.y = y;
}
private String clazz;
private int x;
private Integer y;
@Override
public String toString() {
return "Bean hash is " + this.hashCode() + " ,{" + "clazz='" + clazz + '\'' + ", x=" + x + ", y=" + y + '}';
}
}
public class Main {
public static void main(String[] args) throws Exception {
print("淺拷貝");
shallow();
print();
print("深拷貝");
deep();
}
public static void print(String title) {
System.out.println("*********************" + title + "*********************");
}
public static void print() {
System.out.println("");
}
public static void deep() throws Exception {
DeepCopy A = new DeepCopy(18, 99999, "A", new Bean("三年級", 0, 99999));
System.out.println(A.toString());
ByteArrayOutputStream bos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(bos);
oos.writeObject(A);
oos.flush();
ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray());
ObjectInputStream ois = new ObjectInputStream(bis);
DeepCopy B = (DeepCopy) ois.readObject();
System.out.println(B.toString());
}
public static void shallow() throws Exception {
ShallowCopy A = new ShallowCopy(18, 99999, "A", new Bean("三年級", 0, 99999));
print("觀察 A 和 B 對象的 hash");
System.out.println(A.toString());
ShallowCopy B = (ShallowCopy) A.clone();
System.out.println(B.toString());
print();
print("修改B對象的基本數據類型/String類型,觀察 A 和 B 對象的 hash");
B.setAge(1);
B.setCount(1);
B.setName("B");
System.out.println(A.toString());
System.out.println(B.toString());
print();
print("修改C對象的引用類型中的數據,觀察 A 和 C 對象的 hash");
ShallowCopy C = (ShallowCopy) A.clone();
Bean bean = C.getBean();
bean.setX(1111111);
System.out.println(A.toString());
System.out.println(C.toString());
}
}
測試結果
注意hash值的變化,上述代碼中測試了幾種情況
- 修改
B
對象的基本數據類型/包裝類型/String類型,觀察A
和B
對象的 hash和數據的變化 - 修改
C
對象的引用類型中的數據,觀察A
和C
對象的hash
和數據的變化