由Object類定義的絕大部分方法在本書其他部分討論。而一個特別值得關注的方法是clone( )。clone( )方法創建調用它的對象的一個復制副本。只有那些實現Cloneable接口的類能被復制。
Cloneable接口沒有定義成員。它通常用於指明被創建的一個允許對對象進行位復制(也就是對象副本)的類。如果試圖用一個不支持Cloneable接口的類調用clone( )方法,將引發一個CloneNotSupportedExcepti
on異常。當一個副本被創建時,並沒有調用被復制對象的構造函數。副本僅僅是原對象的一個簡單精確的拷貝。
復制是一個具有潛在危險的操作,因為它可能引起不是你所期望的副作用。例如,假如被復制的對象包含了一個稱為obRef的引用變量,當副本創建時,副 本中的obRef如同原對象中的obRef一樣引用相同的對象。如果副本改變了被obRef引用的對象的內容,那么對應的原對象也將被改變。這里是另一個 例子。如果一個對象打開一個I/O流並被復制,兩個對象將可操作相同的流。而且,如果其中一個對象關閉了流,而另一個對象仍試圖對I/O流進行寫操作的 話,將導致錯誤。
由於復制可能引起問題,因此在Object內,clone( )方法被說明為protected。這就意味着它必須或者被由實現Cloneable的類所定義的方法調用,或者必須被那些類顯式重載以便它是公共的。讓我們看關於下面每一種方法的例子。
下面的程序實現Cloneable接口並定義cloneTest( )方法,該方法在Object中調用clone( )方法:
// Demonstrate the clone() method.
class TestClone implements Cloneable {
int a;
double b;
// This method calls Object's clone().
TestClone cloneTest() {
try {
// call clone in Object.
return (TestClone) super.clone();
} catch(CloneNotSupportedException e) {
System.out.println("Cloning not allowed.");
return this;
}
}
}
class CloneDemo {
public static void main(String args[]) {
TestClone x1 = new TestClone();
TestClone x2;
x1.a = 10;
x1.b = 20.98;
x2 = x1.cloneTest(); // clone x1
System.out.println("x1: " + x1.a + " " + x1.b);
System.out.println("x2: " + x2.a + " " + x2.b);
}
}
這里,方法cloneTest( )在Object中調用clone( )方法並且返回結果。注意由clone( )方法返回的對象必須被強制轉換成它的適當類型(TestClone)。
下面的例子重載clone( )方法以便它能被其類外的程序所調用。為了完成這項功能,它的存取說明符必須是public,如下所示:
// Override the clone() method.
class TestClone implements Cloneable {
int a;
double b;
// clone() is now overridden and is public.
public Object clone() {
try {
// call clone in Object.
return super.clone();
} catch(CloneNotSupportedException e) {
System.out.println("Cloning not allowed.");
return this;
}
}
}
class CloneDemo2 {
public static void main(String args[]) {
TestClone x1 = new TestClone();
TestClone x2;
x1.a = 10;
x1.b = 20.98;
// here, clone() is called directly.
x2 = (TestClone) x1.clone();
System.out.println("x1: " + x1.a + " " + x1.b);
System.out.println("x2: " + x2.a + " " + x2.b);
}
}
由復制帶來的副作用最初一般是比較難發現的。通常很容易想到的是類在復制時是很安全的,而實際卻不是這樣。一般在沒有一個必須的原因的情況下,對任何類都不應該執行Cloneable。
淺復制和深復制來說一下,首先需要了解對象深、淺復制的概念:
深復制:將一個對象復制后,不論是基本數據類型還有引用類型,都是重新創建的。簡單來說,就是深復制進行了完全徹底的復制,而淺復制不徹底。
此處,寫一個深淺復制的例子:
public class Prototype implements Cloneable, Serializable {
- private static final long serialVersionUID = 1L;
- private String string;
- private SerializableObject obj;
- /* 淺復制 */
- public Object clone() throws CloneNotSupportedException {
- Prototype proto = (Prototype) super.clone();
- return proto;
- }
- /* 深復制 */
- public Object deepClone() throws IOException, ClassNotFoundException {
- /* 寫入當前對象的二進制流 */
- ByteArrayOutputStream bos = new ByteArrayOutputStream();
- ObjectOutputStream oos = new ObjectOutputStream(bos);
- oos.writeObject(this);
- /* 讀出二進制流產生的新對象 */
- ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray());
- ObjectInputStream ois = new ObjectInputStream(bis);
- return ois.readObject();
- }
- public String getString() {
- return string;
- }
- public void setString(String string) {
- this.string = string;
- }
- public SerializableObject getObj() {
- return obj;
- }
- public void setObj(SerializableObject obj) {
- this.obj = obj;
- }
- }
- class SerializableObject implements Serializable {
- private static final long serialVersionUID = 1L;
- }