所有的Java类都继承自java.lang.Object。事实上,Object类提供一个clone()方法,可以将一个Java对象复制一份。因此在Java中可以直接使用Object提供的clone()方法来实现对象的克隆。
需要注意的是能够实现克隆的Java类必须实现一个标识接口Cloneable,表示这个Java类支持被复制。
如果一个类没有实现这个接口但是调用了clone()方法,Java编译器将抛出CloneNotSupportedException异常。
如下代码所示:
class ConcretePrototype implements Cloneable { …… public Prototype clone() { Object object = null; try { object = super.clone(); } catch (CloneNotSupportedException exception) { System.err.println("Not support cloneable"); } return (Prototype )object; } …… } |
一般而言,Java语言中的clone()方法满足:
(1) 对任何对象x,都有x.clone() != x,即克隆对象与原型对象不是同一个对象;
(2) 对任何对象x,都有x.clone().getClass() == x.getClass(),即克隆对象与原型对象的类型一样;
(3) 如果对象x的equals()方法定义恰当,那么x.clone().equals(x)应该成立。
为了获取对象的一份拷贝,我们可以直接利用Object类的clone()方法,具体步骤如下:
(1) 在派生类中覆盖基类的clone()方法,并声明为public;
(2) 在派生类的clone()方法中,调用super.clone();
(3)派生类需实现Cloneable接口。
深克隆
在深克隆中,无论原型对象的成员变量是值类型还是引用类型,都将复制一份给克隆对象,深克隆将原型对象的所有引用对象也复制一份给克隆对象。
在Java语言中,如果需要实现深克隆,可以通过序列化(Serialization)等方式来实现。序列化就是将对象写到流的过程,写到流中的对象是原有对象的一个拷贝,而原对象仍然存在于内存中。通过序列化实现的拷贝不仅可以复制对象本身,而且可以复制其引用的成员对象,因此通过序列化将对象写到一个流中,再从流里将其读出来,可以实现深克隆。需要注意的是能够实现序列化的对象其类必须实现Serializable接口,否则无法实现序列化操作。两个类均需要实现Serializable接口。
修改后的附件类Attachment代码如下:
import java.io.*; //附件类 class Attachment implements Serializable { private String name; //附件名 public void setName(String name) { this.name = name; } public String getName() { return this.name; } public void download() { System.out.println("下载附件,文件名为" + name); } } |
工作周报类WeeklyLog不再使用Java自带的克隆机制,而是通过序列化来从头实现对象的深克隆,我们需要重新编写clone()方法,修改后的代码如下:
import java.io.*; //工作周报类 class WeeklyLog implements Serializable { private Attachment attachment; private String name; private String date; private String content; public void setAttachment(Attachment attachment) { this.attachment = attachment; } public void setName(String name) { this.name = name; } public void setDate(String date) { this.date = date; } public void setContent(String content) { this.content = content; } public Attachment getAttachment(){ return (this.attachment); } public String getName() { return (this.name); } public String getDate() { return (this.date); } public String getContent() { return (this.content); } //使用序列化技术实现深克隆 public WeeklyLog 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 (WeeklyLog)ois.readObject(); } } |