您的位置:

Java中实现深拷贝的方法

在Java中,深拷贝是一个很常用的技巧。深拷贝可以创建一个对象的副本,而副本是独立于原始对象的。在Java中,通过克隆或序列化可以实现深拷贝。这篇文章将介绍这两种方法,并提供相关的代码示例。

一、克隆实现深拷贝

Java中Object类提供了一个clone()方法来实现对象的克隆。当一个对象调用clone()方法时,会返回该对象的一个副本,这就是克隆。如果对象中包含了其他对象的引用,只拷贝了引用,而没有对引用对象进行拷贝,这就是浅拷贝。如果要实现深拷贝,则需要对引用对象进行拷贝。

实现深拷贝需要实现Cloneable接口,并覆盖clone()方法。在覆盖clone()方法时需要调用超类的clone()方法,并将需要拷贝的对象的引用也进行拷贝,这样就能够实现深拷贝。


class Foo implements Cloneable {
  private int number;
  private Bar bar;

  public Foo(int number, Bar bar) {
    this.number = number;
    this.bar = bar;
  }

  @Override
  public Foo clone() throws CloneNotSupportedException {
    Foo foo = (Foo) super.clone();
    foo.bar = this.bar.clone();
    return foo;
  }
}

class Bar implements Cloneable {
  private int number;

  public Bar(int number) {
    this.number = number;
  }

  @Override
  public Bar clone() throws CloneNotSupportedException {
    return (Bar) super.clone();
  }
}

在这个例子中,Foo类包含了一个Bar对象,所以在Foo类的clone()方法中需要对Bar对象也进行拷贝。在Bar类的clone()方法中只需要调用超类的clone()方法即可。

二、序列化实现深拷贝

另一种实现深拷贝的方法是通过序列化和反序列化,这种方法比克隆会更慢,但是更加灵活。

在Java中,所有实现了Serializable接口的对象都可以被序列化。序列化意味着将对象转换为字节序列,以便可以将其存储在磁盘上或通过网络传输。反序列化则是将字节序列转换回对象。

为了实现深拷贝,我们可以将对象序列化为字节数组,然后将其反序列化为新的对象。这个过程中,对于序列化的对象及其所有引用的对象,都会进行拷贝,这就实现了深拷贝。


import java.io.*;

class Foo implements Serializable {
  private int number;
  private Bar bar;

  public Foo(int number, Bar bar) {
    this.number = number;
    this.bar = bar;
  }

  public static byte[] serialize(Foo foo) throws IOException {
    ByteArrayOutputStream out = new ByteArrayOutputStream();
    ObjectOutputStream objOut = new ObjectOutputStream(out);
    objOut.writeObject(foo);
    objOut.flush();
    objOut.close();
    return out.toByteArray();
  }

  public static Foo deserialize(byte[] bytes) throws IOException, ClassNotFoundException {
    ByteArrayInputStream in = new ByteArrayInputStream(bytes);
    ObjectInputStream objIn = new ObjectInputStream(in);
    Foo foo = (Foo) objIn.readObject();
    objIn.close();
    return foo;
  }
}

class Bar implements Serializable {
  private int number;

  public Bar(int number) {
    this.number = number;
  }
}

在这个例子中,Foo和Bar都实现了Serializable接口,并提供了一个静态方法来序列化和反序列化Foo对象。

三、总结

在Java中实现深拷贝有两种方法:通过克隆或通过序列化。克隆可以很好地控制拷贝的深度,但是需要实现Cloneable接口,并覆盖clone()方法。序列化则更加灵活,可以拷贝整个对象结构,但是需要实现Serializable接口,并且更慢。

无论使用哪种方法,都需要对对象及其所有引用对象进行拷贝才能实现深拷贝。在实现深拷贝时,尤其需要注意对引用对象的拷贝,否则将无法达到深拷贝的效果。