天天看點

執行個體代碼分析cloneable的使用

分析之前先了解兩個概念

1.淺拷貝

  什麼是淺拷貝

   淺拷貝是按位拷貝對象,它會建立一個新對象,這個對象有着原始對象屬性值的一份精确拷貝。如果屬性是基本類型,拷貝的就是基本類型的值;如果屬性是記憶體位址(引用類型),拷貝的就是記憶體位址 ,是以如果其中一個對象改變了這個位址,就會影響到另一個對象

   如圖:

執行個體代碼分析cloneable的使用

SourceObject有一個int類型的屬性 "field1"和一個引用類型屬性"refObj"(引用ContainedObject類型的對象)。當對SourceObject做淺拷貝時,建立了CopiedObject,它有一個包含"field1"拷貝值的屬性"field2"以及仍指向refObj本身的引用。由于"field1"是基本類型,是以隻是将它的值拷貝給"field2",但是由于"refObj"是一個引用類型, 是以CopiedObject指向"refObj"相同的位址。是以對SourceObject中的"refObj"所做的任何改變都會影響到CopiedObject

2.深拷貝

  深拷貝會拷貝所有的屬性,并拷貝屬性指向的動态配置設定的記憶體。當對象和它所引用的對象一起拷貝時即發生深拷貝。深拷貝相比于淺拷貝速度較慢并且花銷較大,如下圖:

執行個體代碼分析cloneable的使用

圖中,SourceObject有一個int類型的屬性 "field1"和一個引用類型屬性"refObj1"(引用ContainedObject類型的對象)。當對SourceObject做深拷貝時,建立了CopiedObject,它有一個包含"field1"拷貝值的屬性"field2"以及包含"refObj1"拷貝值的引用類型屬性"refObj2" 。是以對SourceObject中的"refObj"所做的任何改變都不會影響到CopiedObject

如何實作淺拷貝呢?看代碼:

public class User implements Cloneable{

public int age;

public String name;

public User(int age, String name) {

this.age = age;

this.name = name;

}

@Override

protected Object clone() throws CloneNotSupportedException {

// TODO Auto-generated method stub

return super.clone();

}

@Override

public String toString() {

return "User [age=" + age + ", name=" + name + ", hashCode()=" + hashCode() + "]";

}

}

public class CloneTest {

public static void main(String[] args) throws Exception {

// TODO Auto-generated method stub

User u1 = new User(15, "Tom");

User u2 = u1;

User u3 = (User)u1.clone();

System.out.println("u1==u2=" + (u1 == u2));

System.out.println("u1==u3=" + (u1 == u3));

System.out.println("u1.equals(u2)=" + (u1.equals(u2)));

System.out.println("u1.equals(u3)=" + (u1.equals(u3)));

System.out.println("u1:" + u1.toString() + "\nu3=" + u3.toString());

u2.age = 20;

u3.age = 30;

System.out.println("u1:" + u1.toString() + "\nu3=" + u3.toString());

}

}

看運作效果:

u1==u2=true//說明指向同一個引用

u1==u3=false//說明是兩個不同引用,clone了一份

u1.equals(u2)=true//說明指向同一個引用

u1.equals(u3)=false//說明是兩個不同引用,clone了一份

u1:User [age=15, name=Tom, hashCode()=366712642]

u3=User [age=15, name=Tom, hashCode()=1829164700]//基本清單與u1值相同,u1、u3兩個對象

had mod u1:User [age=20, name=Tom, hashCode()=366712642]//u2跟u1是同一個引用,是以修改後對u1有影響

u3=User [age=30, name=Tom, hashCode()=1829164700]//u2跟u1不是同一個引用,是以修改後對u1沒有影響

接着看下面的代碼:

public class ShallwCopy implements Cloneable {

    User user;

    long a;

    ShallwCopy(User u, long b) {

        user = u;

        a = b;

    }

    @Override

    protected Object clone() throws CloneNotSupportedException {

        return super.clone();

    }

@Override

public String toString() {

return "ShallwCopy [user=" + user + ", a=" + a + ", hashCode()=" + hashCode() + "]";

}

}

public class CloneTest {

public static void main(String[] args) throws Exception {

// TODO Auto-generated method stub

User u1 = new User(15, "Tom");

User u2 = u1;

User u3 = (User)u1.clone();

System.out.println("==============shallow copy=====================");

ShallwCopy a1 = new ShallwCopy(u1, 10000);

ShallwCopy a2 = a1;

ShallwCopy a3 = (ShallwCopy) a1.clone();

System.out.println("a1==a2=" + (a1 == a2));

System.out.println("a1==a3=" + (a1 == a3));

System.out.println("a1.equals(a2)=" + (a1.equals(a2)));

System.out.println("a1.equals(a3)=" + (a1.equals(a3)));

System.out.println("a1.user==a3.user=" + (a1.user == a3.user));

System.out.println("a1:" + a1.toString() + "\na3=" + a3.toString());

a3.user.age = 40;

System.out.println("a1:" + a1.toString() + "\na3=" + a3.toString());

}

}

運作結果:

==============shallow copy=====================

a1==a2=true

a1==a3=false

a1.equals(a2)=true

a1.equals(a3)=false

a1.user==a3.user=true

a1:ShallwCopy [user=User [age=20, name=Tom, hashCode()=366712642], a=10000, hashCode()=2018699554]

a3=ShallwCopy [user=User [age=20, name=Tom, hashCode()=366712642], a=10000, hashCode()=1311053135]

had mod a1:ShallwCopy [user=User [age=40, name=Tom, hashCode()=366712642], a=10000, hashCode()=2018699554]

a3=ShallwCopy [user=User [age=40, name=Tom, hashCode()=366712642], a=10000, hashCode()=1311053135]

可以看出a1與a3是兩個對象,但是引用的user對象确是同一個,這就如上圖所說的指向了同一個對象refObj,這時候a3修改user的值時,a1也相應的發生了改變。

那怎麼處理才能保證修改a3,而不修改a1的user引用呢,這就是下面說的深拷貝了,看代碼:

public class DeepCopy implements Cloneable {

    User user;

    long a;

    DeepCopy(User u, long b) {

        user = u;

        a = b;

    }

    @Override

    protected Object clone() throws CloneNotSupportedException {

   //  DeepCopy d = (DeepCopy) super.clone();

   // d.user = (User) d.user.clone();

    DeepCopy d = new DeepCopy((User)user.clone(), a);

        return d;

    }

@Override

public String toString() {

return "DeepCopy [user=" + user + ", a=" + a + ", hashCode()=" + hashCode() + "]";

}

}

仔細看下,其實這個類跟上面的ShallowCopy不同的地方在 clone()方法

shawllow的clone方法:return super.clone();

而這個類的clone方法:return new DeepCopy((User)user.clone(), a);

測試類調用:

public class CloneTest {

public static void main(String[] args) throws Exception {

// TODO Auto-generated method stub

User u1 = new User(15, "Tom");

User u2 = u1;

User u3 = (User)u1.clone();

System.out.println("=============deep copy=====================");

DeepCopy d1 = new DeepCopy(u1, 10000);

DeepCopy d2 = d1;

DeepCopy d3 = (DeepCopy) d1.clone();

System.out.println("d1==d2=" + (d1 == d2));

System.out.println("d1==d3=" + (d1 == d3));

System.out.println("d1.equals(d2)=" + (d1.equals(d2)));

System.out.println("d1.equals(d3)=" + (d1.equals(d3)));

System.out.println("d1.user==d3.user=" + (d1.user == d3.user));

System.out.println("d1:" + d1.toString() + "\nd3=" + d3.toString());

d3.user.age = 50;

System.out.println("had mod d1:" + d1.toString() + "\nd3=" + d3.toString());

}

}

看下運作結果:

=============deep copy=====================

d1==d2=true

d1==d3=false

d1.equals(d2)=true

d1.equals(d3)=false

d1.user==d3.user=false

d1:DeepCopy [user=User [age=40, name=Tom, hashCode()=366712642], a=10000, hashCode()=118352462]

d3=DeepCopy [user=User [age=40, name=Tom, hashCode()=1550089733], a=10000, hashCode()=865113938]

had mod d1:DeepCopy [user=User [age=40, name=Tom, hashCode()=366712642], a=10000, hashCode()=118352462]

d3=DeepCopy [user=User [age=50, name=Tom, hashCode()=1550089733], a=10000, hashCode()=865113938]

可以看到d1與d3的user不是同一個引用對象了,是以修改d3的user時,也不會引用到d1的user

好了,以上是個人對cloneable接口與clone方法的代碼實測,如有講的不正确的地方,歡迎指出。