淺拷貝:使用一個已知實例對新創(chuàng)建實例的成員變量逐個賦值,這個方式被稱為淺拷貝摩幔。
深拷貝:不僅只是對成員變量賦值藻茂,而且如果成員變量是引用類型的話,也一并賦值引用類型裳食,然后將新引用拷貝到該成員變量
假設我們新建一個類Person
public class Person {
private String name;
private int age;
public Person(String name, int age) {
this.name = name;
this.age = age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
以往矛市,如果我們要復制的時候,直接如下:
public static void main(String[] args) throws Exception {
Person person1 = new Person("shengfan", 20);
Person person2 = person1; // 淺拷貝
System.out.println("person1:" + person1.toString());
System.out.println("person2:" + person2.toString());
}
輸出:
可以看出诲祸,person2的內(nèi)容和person1一樣浊吏,復制成功而昨。但是這種淺拷貝有個缺點就是如果person1變化了,則復制出來的也會跟著變找田,如下測試:
public static void main(String[] args) throws Exception {
Person person1 = new Person("shengfan", 20);
Person person2 = person1;
System.out.println("person1:" + person1.toString());
System.out.println("person2:" + person2.toString());
person1.setAge(100); // 只是改變person1的年齡
System.out.println("person1:" + person1.toString());
System.out.println("person2:" + person2.toString());
}
雖然上面只是修改了person1的年齡歌憨,但是person2卻也跟著變了,因為主要是person1和person2指向的是堆中的同一個對象墩衙,如下所示:
因此务嫡,當修改person1的值后,person2也跟著改變漆改。而深拷貝就是為了讓person2完全獨立出來心铃,和person1無關聯(lián),不會因為person1的改變而改變挫剑,如下:
深拷貝的實現(xiàn)
Person需要實現(xiàn)Cloneable接口
public class Person implements Cloneable {
private String name;
private int age;
public Person(String name, int age) {
this.name = name;
this.age = age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
public Person clone() {
Person obj = null;
try {
obj = (Person) super.clone();
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
return obj;
}
}
public static void main(String[] args) throws Exception {
Person person1 = new Person("shengfan", 20);
Person person2 = person1.clone();
System.out.println("person1:" + person1.toString());
System.out.println("person2:" + person2.toString());
person1.setAge(100);
System.out.println("person1:" + person1.toString());
System.out.println("person2:" + person2.toString());
}
執(zhí)行結果:
完畢去扣。。樊破。
但是一般對象不會像我們這么簡單厅篓,假設person對象里面還有一個cat對象
public class Cat {
private String name;
public Cat(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
public class Person implements Cloneable {
private String name;
private int age;
private Cat cat;
public Person(String name, int age, Cat cat) {
this.name = name;
this.age = age;
this.cat = cat;
}
public Cat getCat() {
return cat;
}
public void setCat(Cat cat) {
this.cat = cat;
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age + ", cat=" + this.cat.getName() +
'}';
}
public Person clone() {
Person obj = null;
try {
obj = (Person) super.clone();
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
return obj;
}
}
然后運行:
public static void main(String[] args) throws Exception {
Cat cat = new Cat("花花");
Person person1 = new Person("shengfan", 20, cat);
Person person2 = person1.clone();
System.out.println("person1:" + person1.toString());
System.out.println("person2:" + person2.toString());
cat.setName("笨妞");
person1.setCat(cat);
System.out.println("person1:" + person1.toString());
System.out.println("person2:" + person2.toString());
}
結果發(fā)現(xiàn),雖然用了clone方法捶码,然而person2里面的cat還是隨著person1的修改跟著變化羽氮,
原因在于雖然person2是重新new出來的對象,和person1不是一個引用惫恼,然后person2里面的cat的引用依然和person1的cat引用一致档押。可以輸出他們的cat在內(nèi)存中的地址驗證一下:
public static void main(String[] args) throws Exception {
Cat cat = new Cat("花花");
Person person1 = new Person("shengfan", 20, cat);
Person person2 = person1.clone();
cat.setName("笨妞");
person1.setCat(cat);
System.out.println("person1:" + person1.toString());
System.out.println("person2:" + person2.toString());
System.out.println("person1:" + System.identityHashCode(person1));
System.out.println("person2:" + System.identityHashCode(person2));
System.out.println("person1的cat:" + System.identityHashCode(person1.getCat()));
System.out.println("person2的cat:" + System.identityHashCode(person2.getCat()));
}
運行結果:
可以看出祈纯,雖然person1和person2并非同一個引用令宿,但是里面的cat卻是同一個,所以這邊如果對象的成員也是一個引用對象的時候腕窥,需要成員也實現(xiàn)Cloneable接口粒没,并且本身自己的clone需要對成員進行clone:
public class Cat implements Cloneable {
private String name;
public Cat(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Cat clone() {
Cat cat = null;
try {
cat = (Cat) super.clone();
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
return cat;
}
}
public class Person implements Cloneable {
private String name;
private int age;
private Cat cat;
public Person(String name, int age, Cat cat) {
this.name = name;
this.age = age;
this.cat = cat;
}
public Cat getCat() {
return cat;
}
public void setCat(Cat cat) {
this.cat = cat;
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age + ", cat=" + this.cat.getName() +
'}';
}
public Person clone() {
Person obj = null;
try {
obj = (Person) super.clone();
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
obj.setCat(cat.clone()); // 此處需要對引用成員變量clone
return obj;
}
}
運行測試:
public static void main(String[] args) throws Exception {
Cat cat = new Cat("花花");
Person person1 = new Person("shengfan", 20, cat);
Person person2 = person1.clone();
System.out.println("person1:" + person1.toString());
System.out.println("person2:" + person2.toString());
cat.setName("笨妞");
person1.setCat(cat);
System.out.println("person1:" + person1.toString());
System.out.println("person2:" + person2.toString());
System.out.println("person1:" + System.identityHashCode(person1));
System.out.println("person2:" + System.identityHashCode(person2));
System.out.println("person1的cat:" + System.identityHashCode(person1.getCat()));
System.out.println("person2的cat:" + System.identityHashCode(person2.getCat()));
}
結果: