吃透Java基础七(浅拷贝与深拷贝)

一:什么是浅拷贝和深拷贝

  • 浅拷贝:原型对象的成员变量是值类型,将复制一份给克隆对象;如果原型对象的成员变量是引用类型,则将引用对象的地址复制一份给克隆对象,也就是说原型对象和克隆对象的成员变量指向相同的内存地址。也就是说:在浅拷贝中,当对象被复制时只复制它本身和其中包含的值类型的成员变量,而引用类型的成员对象并没有复制。
  • 深拷贝:无论原型对象的成员变量是值类型还是引用类型,都将复制一份给克隆对象,深克隆将原型对象的所有引用对象也复制一份给克隆对象。也就是说:在深克隆中,除了对象本身被复制外,对象所包含的所有成员变量也将复制。
实现对象克隆有两种方式:
  • 实现Cloneable接口并重写Object类中的clone()方法。
  • 实现Serializable接口,通过对象的序列化和反序列化实现克隆,可以实现真正的深度克隆。
二:浅拷贝
浅拷贝只需要实现Cloneable,在需要克隆的地方调用clone方法即可,实现起来比较简单。
public class Inner implements Cloneable {}

public class Outer implements Cloneable { private Inner inner; public Outer(Inner inner) { this.inner = inner; }public Inner getInner() { return inner; } }

public class MyTest {public static void main(String[] args) { Inner inner = new Inner(); Outer outer = new Outer(inner); try { Outer cloneOuter = (Outer) outer.clone(); System.out.println("Outer:" + outer + " cloneOuter:" + cloneOuter); System.out.println("Inner:" + outer.getInner() + " cloneInner:" + cloneOuter.getInner()); } catch (CloneNotSupportedException e) { e.printStackTrace(); } } }

输出:
Outer:Outer@4554617c cloneOuter:Outer@74a14482 Inner:Inner@1540e19d cloneInner:Inner@1540e19d

从输出结果可以看出,浅拷贝只是对当前对象Outer创建了一个新的对象,里面的引用类型Inner还是原对象的地址,并没有重新创建一个对象。
三:深拷贝
在Java语言中,如果需要实现深克隆,可以通过覆盖Object类的clone()方法实现,也可以通过序列化(Serialization)等方式来实现。
深拷贝实现方式一:通过覆盖Object的clone方法实现
此种方法通过重写Object中的clone方法,并在其内部又对引用类型拷贝来实现的深拷贝,如果引用类型里面还包含很多引用类型,或者内层引用类型的类里面又包含引用类型,使用clone方法就会很麻烦。
public class Inner implements Cloneable { @Override protected Object clone() throws CloneNotSupportedException { return super.clone(); } }public class Outer implements Cloneable { private Inner inner; public Outer(Inner inner) { this.inner = inner; }@Override protected Object clone() throws CloneNotSupportedException { Outer outer = (Outer) super.clone(); outer.inner = (Inner) outer.inner.clone(); return outer; }public Inner getInner() { return inner; } }

public class MyTest {public static void main(String[] args) { Inner inner = new Inner(); Outer outer = new Outer(inner); try { Outer cloneOuter = (Outer) outer.clone(); System.out.println("Outer:" + outer + " cloneOuter:" + cloneOuter); System.out.println("Inner:" + outer.getInner() + " cloneInner:" + cloneOuter.getInner()); } catch (CloneNotSupportedException e) { e.printStackTrace(); } } }

运行输出:
Outer:Outer@4554617c cloneOuter:Outer@74a14482 Inner:Inner@1540e19d cloneInner:Inner@677327b6

深拷贝实现方式二:通过序列化方式实现
如果引用类型里面还包含很多引用类型,或者内层引用类型的类里面又包含引用类型,使用clone方法就会很麻烦。这时我们可以用序列化的方式来实现对象的深克隆。
public class Inner implements Serializable {}public class Outer implements Serializable { private Inner inner; public Outer(Inner inner) { this.inner = inner; }public Inner getInner() { return inner; } }

public class MyTest {public static void main(String[] args) throws IOException, ClassNotFoundException { Inner inner = new Inner(); Outer outer = new Outer(inner); ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(); ObjectOutputStream objectOutputStream = new ObjectOutputStream(byteArrayOutputStream); objectOutputStream.writeObject(outer); ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(byteArrayOutputStream.toByteArray()); ObjectInputStream objectInputStream = new ObjectInputStream(byteArrayInputStream); Outer streamOuter = (Outer) objectInputStream.readObject(); System.out.println("Outer:" + outer + " streamOuter:" + streamOuter); System.out.println("Inner:" + outer.getInner() + " streamInner:" + streamOuter.getInner()); } }

【吃透Java基础七(浅拷贝与深拷贝)】运行输出:
Outer:Outer@6d6f6e28 streamOuter:Outer@4b67cf4d Inner:Inner@135fbaa4 streamInner:Inner@7ea987ac

    推荐阅读