java中clone()方法的的作用是Creates and returns a copy of this object。
The general intent is that, for any object {@code x}, the expression:
x.clone() != x,will be true
x.clone().getClass() == x.getClass(),will be true
x.clone().equals(x),will be true 其中第一点必须是不等的,也就是说复制出来的对象有自己单独的内存地址;第二点表明clone出来的java对象的java类型是相同的,不是强制要求的;第三点表明clone出来的对象使用equals方法是相等的,但不是强制要求的。
下来我们来做个试验:
public class Student implements Cloneable{
private String name;
private int age;
private Address address;
public Student(String name, int age, Address address){
this.name = name;
this.age = age;
this.address = address;
}
@Override
protected Object clone() throws CloneNotSupportedException{
return super.clone();
}
}
public class Address {
private String city;
private String country;
public Address(String city, String country) {
this.city = city;
this.country = country;
}
}
测试一下,是不是如我们jdk文档所说的
public class Test {
public static void main(String[] args) throws CloneNotSupportedException{
Address address = new Address("wuhan","china");
Student student = new Student("jack",25,address);
Student cloneStuednt = (Student) student.clone();
System.out.println(student != cloneStuednt);
System.out.println(student.getClass() == cloneStuednt.getClass());
System.out.println(cloneStuednt.equals(student));
}
}
输出:
true
true
false
如果这个时候我们更改原对象中的状态又会发生什么呢?
public class Test {
public static void main(String[] args) throws CloneNotSupportedException{
Address address = new Address("wuhan","china");
Student student = new Student("jack",25,address);
Student cloneStuednt = (Student) student.clone();
student.getAddress().setCity("nanjing");
System.out.println(cloneStuednt.getAddress().getCity());
}
}
输出
nanjing
我们会发现如果改变了原对象,那么克隆出来的对象也发生了变化,在某些情况下,我们并不希望这种事情发生。其实这是由于浅克隆造成的,那么如何可以避免浅克隆
deep clone
上面这个例子浅克隆的原因是由于Address对象没有实现Cloneable接口,如果Address对象也实现了这个接口,并重写了clone()方法,便可以解决这个问题。
public class Address implements Cloneable{
private String city;
private String country;
public Address(String city, String country){
this.city = city;
this.country = country;
}
@Override
protected Object clone() throws CloneNotSupportedException{
return super.clone();
}
public String getCity(){
return city;
}
public void setCity(String city){
this.city = city;
}
public String getCountry(){
return country;
}
public void setCountry(String country){
this.country = country;
}
}
public class Student implements Cloneable{
private String name;
private int age;
private Address address;
public Student(String name, int age, Address address){
this.name = name;
this.age = age;
this.address = address;
}
@Override
protected Object clone() throws CloneNotSupportedException{
Student clone = (Student) super.clone();
clone.setAddress((Address) clone.getAddress().clone());
return clone;
}
public String getName(){
return name;
}
public void setName(String name){
this.name = name;
}
public int getAge(){
return age;
}
public void setAge(int age){
this.age = age;
}
public Address getAddress(){
return address;
}
public void setAddress(Address address){
this.address = address;
}
}
最后我们再次运行一下测试类,会发现这次是我们所期望的结果
public class Test {
public static void main(String[] args) throws CloneNotSupportedException{
Address address = new Address("wuhan","china");
Student student = new Student("jack",25,address);
Student cloneStuednt = (Student) student.clone();
student.getAddress().setCity("nanjing");
System.out.println(cloneStuednt.getAddress().getCity());
}
}
输出
wuhan
这的确完成了deep clone,但我们并不推荐这样去做,在实际的项目中很可能也无法让每个类都去实现Cloneable接口重写clone方法。那么是否还有其他的方法可以完成深拷贝吗? apache的 org.apache.commons.lang.SerializationUtils类有一种方法,使用序列化和反序列化的方法实现了深拷贝。主要的代码是:
ByteArrayOutputStreambos= new ByteArrayOutputStream();ObjectOutputStream oos = new ObjectOutputStream(bos);oos.writeObject(object);
oos.flush();
oos.close();
bos.close();byte[]byteData= bos.toByteArray();ByteArrayInputStreambais= new ByteArrayInputStream(byteData);(Object) object = (Object) new ObjectInputStream(bais).readObject();
这种方法我感觉应该是最简单的方法实现深拷贝的方法。
另外还有另外一种方法实现了深拷贝,通过java反射区一个个去get/set设置java对象。