天天看点

实例代码分析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方法的代码实测,如有讲的不正确的地方,欢迎指出。