更多 Java 高級(jí)知識(shí)方面的文章,請(qǐng)參見(jiàn)文集《Java 高級(jí)知識(shí)》
首先看下面這一段代碼:
Student s = new Student();
Student s1= s;
- 如果是在 C++ 中,會(huì)調(diào)用拷貝構(gòu)造函數(shù),復(fù)制一份新的對(duì)象,由
s1
指向該新的對(duì)象 - 如果是在 Java 中,
s1
為引用,仍然指向s
的內(nèi)存區(qū)域,即同一份對(duì)象
在下面的代碼中:
c1
和 c2
剛開(kāi)始分別指向兩個(gè)不同的對(duì)象,c2 = c1;
使得 c2
指向 c1
的對(duì)象,因此 c2
原本指向的對(duì)象 B 失去引用,會(huì)被 GC 回收。
public class Clone_Test {
public static void main(String[] args) {
MyClone c1 = new MyClone("A");
MyClone c2 = new MyClone("B");
// c2 指向 c1 的對(duì)象
// c2 原本指向的對(duì)象 B 失去引用,會(huì)被 GC 回收
c2 = c1;
c2.name = "C";
System.out.println(c1.name); // 輸出 C
System.out.println(c2.name); // 輸出 C
}
}
class MyClone {
public String name;
public MyClone(String name) {
this.name = name;
}
}
Clone 的實(shí)現(xiàn)
實(shí)現(xiàn) Cloneable
接口,并重寫(xiě) clone()
方法。
關(guān)于 Cloneable 接口
-
Cloneable
是一個(gè)空接口。并不包含任何方法,更像是一個(gè)標(biāo)記。
public interface Cloneable {
}
-
implements Cloneable
指示調(diào)用clone()
方法時(shí)可以合法地對(duì)該對(duì)象進(jìn)行按字段的復(fù)制
A class implements the <code>Cloneable</code> interface to indicate to the clone() method that it is legal for that method to make a field-for-field copy of instances of that class.
- 如果不實(shí)現(xiàn)
Cloneable
接口,此時(shí)在調(diào)用clone()
方法時(shí),會(huì)拋出異常CloneNotSupportedException
關(guān)于 Clone 方法
-
clone()
方法是Object
類(lèi)中自帶的 native 方法,參見(jiàn) Java Object 類(lèi)中有哪些方法 -
clone()
方法默認(rèn)是淺復(fù)制- 對(duì)基礎(chǔ)類(lèi)型有用
- 對(duì)引用類(lèi)型只是復(fù)制引用,不是真正地復(fù)制對(duì)象
-
clone()
方法不會(huì)調(diào)用構(gòu)造方法 - 幾個(gè)基本的比較:
x.clone() == x // false,因?yàn)閯?chuàng)建出了一個(gè)新對(duì)象
x.clone().getClass() == x.getClass() // true
x.clone().equals(x) // 一般為false,取決于 clone() 方法的具體實(shí)現(xiàn)
例如將上述的代碼重構(gòu)為:
public class Clone_Test {
public static void main(String[] args) {
MyClone c1 = new MyClone("A");
MyClone c2 = new MyClone("B");
// c2 指向一個(gè)新對(duì)象
// c2 原本指向的對(duì)象 B 失去引用,會(huì)被 GC 回收
c2 = (MyClone) c1.clone();
c2.name = "C";
System.out.println(c1.name); // 輸出 A
System.out.println(c2.name); // 輸出 C
}
}
class MyClone implements Cloneable {
public String name;
public MyClone(String name) {
this.name = name;
}
public Object clone() {
MyClone c = null;
try {
c = (MyClone) super.clone();
} catch (Exception e) {
}
return c;
}
}
淺拷貝 VS 深拷貝
Object
類(lèi)中自帶的clone()
方法是淺拷貝,即 field-for-field copy。
如果某個(gè)對(duì)象中包含一些其他對(duì)象,例如 Stream
,淺拷貝只會(huì)復(fù)制該 Stream
對(duì)象的引用,該 Stream
對(duì)象會(huì)被兩個(gè)對(duì)象 c1
和 c2
共享,可能會(huì)導(dǎo)致問(wèn)題。
要想實(shí)現(xiàn)深拷貝,即對(duì)象中包含的其他對(duì)象也會(huì)復(fù)制出一份新的對(duì)象,可以通過(guò)如下方式:
- 需要重寫(xiě)
clone()
方法,實(shí)現(xiàn)深拷貝 - 通過(guò)序列化方式
ObjectOutputStream ObjectInputStream
- 通過(guò)第三方庫(kù)