一:什么是浅拷贝和深拷贝
- 浅拷贝:原型对象的成员变量是值类型,将复制一份给克隆对象;如果原型对象的成员变量是引用类型,则将引用对象的地址复制一份给克隆对象,也就是说原型对象和克隆对象的成员变量指向相同的内存地址。也就是说:在浅拷贝中,当对象被复制时只复制它本身和其中包含的值类型的成员变量,而引用类型的成员对象并没有复制。
- 深拷贝:无论原型对象的成员变量是值类型还是引用类型,都将复制一份给克隆对象,深克隆将原型对象的所有引用对象也复制一份给克隆对象。也就是说:在深克隆中,除了对象本身被复制外,对象所包含的所有成员变量也将复制。
- 实现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