java中的对象拷贝

来源:互联网 发布:ubuntu u盘挂载 编辑:程序博客网 时间:2024/06/04 01:14

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));    }}输出:truetruefalse

如果这个时候我们更改原对象中的状态又会发生什么呢?

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类有一种方法,使用序列化和反序列化的方法实现了深拷贝。主要的代码是:

ByteArrayOutputStream bos = new ByteArrayOutputStream();ObjectOutputStream oos = new ObjectOutputStream(bos);oos.writeObject(object);oos.flush();oos.close();bos.close();byte[] byteData = bos.toByteArray();ByteArrayInputStream bais = new ByteArrayInputStream(byteData);(Object) object = (Object) new ObjectInputStream(bais).readObject();

这种方法我感觉应该是最简单的方法实现深拷贝的方法。

另外还有另外一种方法实现了深拷贝,通过java反射区一个个去get/set设置java对象。

原创粉丝点击