今天在看阿里巴巴java开发手册时看到这么一个规范不太理解因为平时clone方法也用的比较少。所以去看了下java的api文档。总结了一下java中clone()方法。
我们在对象的赋值其实就是复制了引用对象的内存地址:
@Test
public void testassign(){
Person p1=new Person();
p1.setAge(31);
p1.setName("Peter");
Person p2=p1;
System.out.println(p1==p2);//true
但是当我们想一开始对象有相同的初始状态,然后修改不同的属性方法,对象之间相互不影响,这时候就需要java中的clone()方法了。
java中的api文档是这么写的
我们可以看到克隆后的对象与被复制的对象是不相等的,==的比较是地址的比较,说明对象的clone没有引用之前的地址了。后面的叙述有点不容易理解,其实说白了就是克隆clone()方法中浅拷贝和深度拷贝的问题。如果一个实体类想要克隆一个对象,首先要实现Cloneable()的接口,重写clone()方法。当该实体类只有基本类型字段或不变对象的引用时,我们不需要在clone()方法中重写该成员属性的克隆逻辑,如果有的话,则需要重写该成员属性的克隆逻辑。现在上代码。
@Data
public class Person implements Cloneable {
private String name;
private Integer age;
private Address address;
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
}
@Test
public void testShallowCopy() throws Exception{
Person p1=new Person();
p1.setAge(31);
p1.setName("Peter");
Person p2=(Person) p1.clone();
System.out.println(p1==p2);//false
p2.setName("Jacky");
System.out.println("p1="+p1);//p1=Person [name=Peter, age=31]
System.out.println("p2="+p2);//p2=Person [name=Jacky, age=31]
}
如果是基本数据类型的成员变量,我们克隆之后彼此的属性是可以相互不影响的,接着看:
如果增加一个自定义的类对象时
@Data
public class Address {
private String type;
private String value;
}
测试
@Test
public void testShallowCopy() throws Exception{
Address address=new Address();
address.setType("Home");
address.setValue("北京");
Person p1=new Person();
p1.setAge(31);
p1.setName("Peter");
p1.setAddress(address);
Person p2=(Person) p1.clone();
System.out.println(p1==p2);//false
p2.getAddress().setType("Office");
System.out.println("p1="+p1);
System.out.println("p2="+p2);
}
输出结果:
false
p1=Person(name=Peter, age=31, address=Address(type=Office, value=北京))
p2=Person(name=Peter, age=31, address=Address(type=Office, value=北京))
这是我们可以发现当我对p2对象进行操作的时候,p1对象中Address的值也发生了改变,这就是浅拷贝。其实p1对象中Address属性还是引用了p2中的地址。所以p2一改p1也跟着改变了。
前面实例中是浅拷贝和深拷贝的典型用例。
浅拷贝:被复制对象的所有值属性都含有与原来对象的相同,而所有的对象引用属性仍然指向原来的对象。
深拷贝:在浅拷贝的基础上,所有引用其他对象的变量也进行了clone,并指向被复制过的新对象。
也就是说,一个默认的clone()方法实现机制,仍然是赋值。
如果一个被复制的属性都是基本类型,那么只需要实现当前类的cloneable机制就可以了,此为浅拷贝。
如果被复制对象的属性包含其他实体类对象引用,那么这些实体类对象都需要实现cloneable接口并覆盖clone()方法。
@Data
public class Address implements Cloneable {
private String type;
private String value;
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
}
这样还不够,Person的clone()需要显式地clone其引用成员。
@Data
public class Person implements Cloneable {
private String name;
private Integer age;
private Address address;
@Override
protected Object clone() throws CloneNotSupportedException {
Object obj=super.clone();
Address a=((Person)obj).getAddress();
((Person)obj).setAddress((Address) a.clone());
return obj;
}
}
重新跑前面的测试用例:
false
p1=Person(name=Peter, age=31, address=Address(type=Home, value=北京))
p2=Person(name=Peter, age=31, address=Address(type=Office, value=北京)
现在克隆后的对象的属性就也不会跟着改变了