1、拷贝的引入
1.1 引用拷贝
1)创建一个指向对象的引用变量的拷贝。
2)二者的引用是同一个对象,并没有创建出一个新的对象。
3)因为是同一个对象的引用,所以两者改一个,另一个对象的值也随之改变。
Teacher teacher = new Teacher("Taylor",26);
Teacher otherteacher = teacher;
System.out.println(teacher);
System.out.println(otherteacher);
输出结果:
blog.Teacher@355da254
blog.Teacher@355da254
结果分析:由输出结果可以看出,它们的地址值是相同的,那么它们肯定是同一个对象。teacher和otherteacher的只是引用而已,都指向了一个相同的对象Teacher(“Taylor”,26)。 这就叫做引用拷贝。
1.2 对象拷贝
创建对象本身的一个副本。
Teacher teacher = new Teacher("Swift",26);
Teacher otherteacher = (Teacher)teacher.clone();
System.out.println(teacher);
System.out.println(otherteacher);
输出结果:
blog.Teacher@355da254
blog.Teacher@4dc63996
结果分析:由输出结果可以看出,它们的地址是不同的,也就是说创建了新的对象, 而不是把原对象的地址赋给了一个新的引用变量,这就叫做对象拷贝。
注:深拷贝和浅拷贝都是对象拷贝
2、浅拷贝介绍
1)对于基本数据类型的成员变量,浅拷贝会直接进行值传递,就是将该属性值复制一份给新的对象
2)对于引用数据类型的成员变量,比如说成员变量是某个数组、某个类的对象等,那么浅拷贝会进行引用传递,也就是只是将该成员变量的引用值(内存地址)复制一份给新的对象。
实际上两个对象的该成员变量都指向同一个实例。在这种情况下,在一个对象中修改该成员变量会影响到另一个对象的该成员变量值。
3)设计模式中的原型模式就是浅拷贝。
4)浅拷贝是使用默认的 clone()方法来实现,sheep = (Sheep) super.clone();
代码演示:
public class Sheep implements Cloneable {
private String name;
private int age;
public Sheep friend; //是对象, 克隆是会如何处理
public Sheep(String name, int age) {
this.name = name;
this.age = age;
}
//克隆该实例,使用默认的clone方法来完成
@Override
protected Object clone() {
Sheep sheep = null;
try {
sheep = (Sheep)super.clone();
} catch (Exception e) {
// TODO: handle exception
System.out.println(e.getMessage());
}
// TODO Auto-generated method stub
return sheep;
}
//get,set,toString等方法省略
}
public static void main(String[] args) {
System.out.println("原型模式完成对象的创建");
Sheep sheep = new Sheep("tom", 1);
sheep.friend = new Sheep("jack", 2);
Sheep sheep2 = (Sheep)sheep.clone(); //克隆
System.out.println(sheep.hashCode() == sheep2.hashCode());
System.out.println(sheep.friend.hashCode() == sheep2.friend.hashCode());
}
输出结果为:false,true
结果分析: 两个引用sheep和sheep2指向不同的两个对象,但是两个引用sheep和sheep2中的friend引用
指向的是同一个对象,所以说明是浅拷贝。
简而言之,浅拷贝仅仅复制所考虑的对象,而不复制它所引用的对象。
3、深拷贝介绍
1)复制对象的所有基本数据类型的成员变量值。
2)为所有引用数据类型的成员变量申请存储空间,并复制每个引用数据类型成员变量所引用的对象,直到该对象可达的所有对象。也就是说,对象进行深拷贝要对整个对象进行拷贝。
3)深拷贝实现方式1:重写clone方法来实现深拷贝
4)深拷贝实现方式2:通过对象序列化实现深拷贝(推荐)
代码演示:
public class DeepProtoType implements Serializable, Cloneable{
public String name; //String 属性
public DeepCloneableTarget deepCloneableTarget;// 引用类型
public DeepProtoType() {
super();
}
//深拷贝 - 方式 1 使用clone 方法
@Override
protected Object clone() throws CloneNotSupportedException {
Object deep = null;
//这里完成对基本数据类型(属性)和String的克隆
deep = super.clone();
//对引用类型的属性,进行单独处理
DeepProtoType deepProtoType = (DeepProtoType)deep;
deepProtoType.deepCloneableTarget = (DeepCloneableTarget)deepCloneableTarget.clone();
// TODO Auto-generated method stub
return deepProtoType;
}
//深拷贝 - 方式2 通过对象的序列化实现 (推荐)
public Object deepClone() {
//创建流对象
ByteArrayOutputStream bos = null;
ObjectOutputStream oos = null;
ByteArrayInputStream bis = null;
ObjectInputStream ois = null;
try {
//序列化
bos = new ByteArrayOutputStream();
oos = new ObjectOutputStream(bos);
oos.writeObject(this); //当前这个对象以对象流的方式输出
//反序列化
bis = new ByteArrayInputStream(bos.toByteArray());
ois = new ObjectInputStream(bis);
DeepProtoType copyObj = (DeepProtoType)ois.readObject();
return copyObj;
} catch (Exception e) {
// TODO: handle exception
e.printStackTrace();
return null;
} finally {
//关闭流
try {
bos.close();
oos.close();
bis.close();
ois.close();
} catch (Exception e2) {
// TODO: handle exception
System.out.println(e2.getMessage());
}
}
}
}
public class DeepCloneableTarget implements Serializable, Cloneable {
private static final long serialVersionUID = 1L;
private String cloneName;
private String cloneClass;
//构造器
public DeepCloneableTarget(String cloneName, String cloneClass) {
this.cloneName = cloneName;
this.cloneClass = cloneClass;
}
//因为该类的属性,都是String , 因此我们这里使用默认的clone完成即可
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
}
public static void main(String[] args) throws Exception {
// TODO Auto-generated method stub
DeepProtoType p = new DeepProtoType();
p.name = "宋江";
p.deepCloneableTarget = new DeepCloneableTarget("大", "小");
//方式1 完成深拷贝
// DeepProtoType p2 = (DeepProtoType) p.clone();
// System.out.println(p.deepCloneableTarget.hashCode()==
// p2.deepCloneableTarget.hashCode());
//方式2 完成深拷贝
DeepProtoType p2 = (DeepProtoType) p.deepClone();
System.out.println(p.hashCode()==p2.hashCode());
System.out.println(p.deepCloneableTarget.hashCode()==
p2.deepCloneableTarget.hashCode());
}
输出结果为:false,false
结果分析: 两个引用 p 和 p2
指向不同的两个对象,两个引用 p.deepCloneableTarget 和 p.deepCloneableTarget
也指向不同的两个对象,所以说明是深拷贝。
简而言之,深拷贝把要复制的对象所引用的对象都复制了一遍。
参考:浅拷贝与深拷贝