浅克隆
浅克隆只复制当前对象的所有基本数据类型,以及相应的引用变量,但没有复制引用变量指向的实际对象,也就是只复制了引用变量的内存地址。
重写Object的clone方法,然后实现Cloneable接口。被克隆的类必须实现Cloneable接口,否则如果我们将在对象上调用clone()时,JVM将抛出CloneNotSupportedException。
Main.java
public class Main {
public static void main(String[] args) {
Person source = new Person();
Person target = null;
try {
target = source.clone();
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
System.out.println("被克隆对象: " + source.hashCode() + "\n" +
"被克隆对象内的dog属性: " + source.getDog().hashCode());
System.out.println("===============================");
System.out.println("克隆出来的对象: " + target.hashCode() + "\n" +
"克隆出来的对象内的dog属性: " + target.getDog().hashCode());
}
}
Person.java
public class Person implements Cloneable {
private Dog dog;
public Dog getDog() {
return dog;
}
public Person() {
this.dog = new Dog();
}
@Override
protected Person clone() throws CloneNotSupportedException {
return (Person) super.clone();
}
}
输出
被克隆对象: 1163157884
被克隆对象内的dog属性: 1956725890
===============================
克隆出来的对象: 356573597
克隆出来的对象内的dog属性: 1956725890
我们发现两个对象的dog属性在内存中的标识并没有改变,因为浅克隆只拷贝了引用变量所指向堆内存的内存地址。
深克隆
深克隆彻底复制了当前对象,此对象与母对象在任何引用路径上都不存在共享的实例对象。也就是说深克隆把所有引用变量所指向的变量都拷贝了一份。
先把对象序列化到流中,然后再把对象从流中取出来,取出来的对象和原来的对象就没有联系了,引用变量所指向的地址也和原来的对象不同,这样就达到了深克隆的目的。不过这种方式的效率会比较低。
除了通过序列化成流进行深克隆,还可以通过手动设置属性的值实现克隆。
下面演示通过序列化成流进行深克隆。
Main.java
public class Main {
public static void main(String[] args) {
Person source = new Person();
Person target = (Person) CloneUtils.clone(source);
System.out.println("被克隆对象: " + source.hashCode() + "\n" +
"被克隆对象内的dog属性: " + source.getDog().hashCode());
System.out.println("===============================");
System.out.println("克隆出来的对象: " + target.hashCode() + "\n" +
"克隆出来的对象内的dog属性: " + target.getDog().hashCode());
}
}
Person.java
public class Person implements Serializable {
private Dog dog;
public Dog getDog() {
return dog;
}
public Person() {
this.dog = new Dog();
}
}
Dog.java
public class Dog implements Serializable {
private String name;
public Dog() {
this.name = "大黄";
}
}
CloneUtils.java
public class CloneUtils {
public static Object clone(Object obj) {
Object cloneObj = null;
try {
//写入字节流
ByteArrayOutputStream out = new ByteArrayOutputStream();
ObjectOutputStream obs = new ObjectOutputStream(out);
obs.writeObject(obj);
obs.close();
//分配内存,写入原始对象,生成新对象
ByteArrayInputStream ios = new ByteArrayInputStream(out.toByteArray());
ObjectInputStream ois = new ObjectInputStream(ios);
//返回生成的新对象
cloneObj = ois.readObject();
ois.close();
} catch (Exception e) {
e.printStackTrace();
}
return cloneObj;
}
}
输出
被克隆对象: 1836019240
被克隆对象内的dog属性: 325040804
===============================
克隆出来的对象: 363771819
克隆出来的对象内的dog属性: 2065951873
由输出结果可以看出引用变量dog的值已经指向了另外的一个堆内存地址,即通过序列化成流实现了深克隆。