0%

Java对象克隆

1. clone对象使用原因

在实际编程过程中,我们常常要遇到这种情况:有一个对象 A,在某一时刻 A 中已经包含了一些有效值,此时可能会需要一个和 A 完全相同新对象 B,并且此后对 B 任何改动都不会影响到 A 中的值,也就是说, A 与 B 是两个独立的对象,但 B 的初始值是由 A 对象确定的。在 Java 语言中,用简单的赋值语句是不能满足这种需求的。要满足这种需求虽然有很多途径,但实现 clone()方法是其中最简单,也是最高效的手段。

2. new对象与clone对象的区别

  • new 操作符的本意是分配内存。程序执行到 new 操作符时,首先去看 new 操作符后面的类型,因为知道了类型,才能知道要分配多大的内存空间。分配完内存之后,再调用构造函数,填充对象的各个域,这一步叫做对象的初始化,构造方法返回后,一个对象创建完毕,可以把他的引用(地址)发布到外部,在外部就可以使用这个引用操纵这个对象。
  • clone 在第一步是和 new 相似的,都是分配内存,调用 clone 方法时,分配的内存和原对象(即调用 clone 方法的对象)相同,然后再使用原对象中对应的各个域,填充新对象的域,填充完成之后, clone 方法返回,一个新的相同的对象被创建,同样可以把这个新对象的引用发布到外部。

3. clone对象的使用

3.1 浅克隆

在浅克隆中,被复制的对象的所有普通成员变量都具有与原来的对象相同的值,而所有的对其他对对象的引用仍然指向原来的对象。换言之,浅克隆仅仅复制所考虑的对象,而不复制它所引用的成员对象。

在这里插入图片描述
代码示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
//注意:要克隆的类需要实现Cloneable接口
public class Email implements Cloneable {

private Attachment attachment = null;

public Email() {
this.attachment = new Attachment();
}

@Override
public Email clone() {
Email clone = null;
try{
clone = (Email)super.clone();
} catch(CloneNotSupportedException e){
System.out.println("CLone failture!");
}
return clone;
}

public Attachment getAttachment() {
return attachment;
}

public void setAttachment(Attachment attachment) {
this.attachment = attachment;
}
}
1
2
3
public class Attachment implements Cloneable {

}

测试代码:

1
2
3
4
5
6
7
8
9
public class MyTest {
public static void main(String[] args) {
Email email = new Email();
System.out.println("email:" + email.hashCode() + ", attrament:" + email.getAttachment().hashCode());
Email copy = email.clone();
System.out.println("copy email:" + copy.hashCode() +
", copy attrament:" + copy.getAttachment().hashCode());
}
}

运行结果:

1
2
email:2018699554, attrament:1311053135
copy email:118352462, copy attrament:1311053135

3.2 深克隆

在深克隆中被复制对象的所有普通成员变量也都含有与原来对象相同的值,其中的引用对象也被复制。换言之,深克隆把要复制的对象所引用的对象都复制了一遍。

在这里插入图片描述
方法一(将克隆对象的引用属性都克隆一遍):

修改上述类 Email 的 clone 方法如下所示:

1
2
3
4
5
6
7
8
9
10
11
12
@Override
public Email clone() {
Email clone = null;
try{
clone = (Email)super.clone();
//拷贝引用属性
clone.setAttachment(attachment.clone());
} catch(CloneNotSupportedException e){
System.out.println("CLone failture!");
}
return clone;
}

修改 Attachment 类如下所示:

1
2
3
4
5
6
7
8
9
10
11
12
public class Attachment implements Cloneable {

public Attachment clone() {
Attachment clone = null;
try{
clone = (Attachment)super.clone();
} catch(CloneNotSupportedException e){
System.out.println("CLone failture!");
}
return clone;
}
}

运行测试结果如下:

1
2
email:2018699554, attrament:1311053135
copy email:118352462, copy attrament:1550089733

方法二(序列化):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
//注意:要克隆的类需要实现序列化接口
public class Email implements Serializable {

private static final long serialVersionUID = 1L;

private Attachment attachment = null;

public Email() {
this.attachment = new Attachment();
}

public Object deepClone() throws IOException, ClassNotFoundException, OptionalDataException {
// 将对象写入流中
ByteArrayOutputStream bao = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(bao);
oos.writeObject(this);

// 将对象从流中取出
ByteArrayInputStream bis = new ByteArrayInputStream(bao.toByteArray());
ObjectInputStream ois = new ObjectInputStream(bis);
return ois.readObject();
}

public Attachment getAttachment() {
return this.attachment;
}

public void setAttachment(Attachment attachment) {
this.attachment = attachment;
}

}
1
2
3
4
5
public class Attachment implements Serializable {

private static final long serialVersionUID = 1L;

}

运行测试结果如下:

1
2
email:2018699554, attrament:1311053135
copy email:1831932724, copy attrament:1747585824
------ 本文结束------