实例代码分析cloneable的使用

来源:互联网 发布:阴司守门人网络剧资源 编辑:程序博客网 时间:2024/06/04 19:28

分析之前先了解两个概念

1.浅拷贝

  什么是浅拷贝
   浅拷贝是按位拷贝对象,它会创建一个新对象,这个对象有着原始对象属性值的一份精确拷贝。如果属性是基本类型,拷贝的就是基本类型的值;如果属性是内存地址(引用类型),拷贝的就是内存地址 ,因此如果其中一个对象改变了这个地址,就会影响到另一个对象

   如图:

    

SourceObject有一个int类型的属性 "field1"和一个引用类型属性"refObj"(引用ContainedObject类型的对象)。当对SourceObject做浅拷贝时,创建了CopiedObject,它有一个包含"field1"拷贝值的属性"field2"以及仍指向refObj本身的引用。由于"field1"是基本类型,所以只是将它的值拷贝给"field2",但是由于"refObj"是一个引用类型, 所以CopiedObject指向"refObj"相同的地址。因此对SourceObject中的"refObj"所做的任何改变都会影响到CopiedObject


2.深拷贝

  深拷贝会拷贝所有的属性,并拷贝属性指向的动态分配的内存。当对象和它所引用的对象一起拷贝时即发生深拷贝。深拷贝相比于浅拷贝速度较慢并且花销较大,如下图:

  

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

0 0