更新時(shí)間:2022-12-01 11:56:01 來(lái)源:動(dòng)力節(jié)點(diǎn) 瀏覽1479次
對(duì)象克隆是指創(chuàng)建對(duì)象的精確副本。它創(chuàng)建當(dāng)前對(duì)象類的新實(shí)例,并使用該對(duì)象相應(yīng)字段的內(nèi)容來(lái)初始化其所有字段。
在 Java 中,沒(méi)有用于創(chuàng)建對(duì)象副本的運(yùn)算符。與 C++ 不同,在 Java 中,如果我們使用賦值運(yùn)算符,那么它將創(chuàng)建引用變量的副本而不是對(duì)象。這可以舉個(gè)例子來(lái)解釋。下面的程序演示了相同的內(nèi)容。
// Java program to demonstrate that assignment operator
// only creates a new reference to same object
import java.io.*;
// A test class whose objects are cloned
class Test {
int x, y;
Test()
{
x = 10;
y = 20;
}
}
// Driver Class
class Main {
public static void main(String[] args)
{
Test ob1 = new Test();
System.out.println(ob1.x + " " + ob1.y);
// Creating a new reference variable ob2
// pointing to same address as ob1
Test ob2 = ob1;
// Any change made in ob2 will
// be reflected in ob1
ob2.x = 100;
System.out.println(ob1.x + " " + ob1.y);
System.out.println(ob2.x + " " + ob2.y);
}
}
輸出
10 20
100 20
100 20
要制作其對(duì)象副本的類必須在其中或其父類之一中具有公共克隆方法。
每個(gè)實(shí)現(xiàn) clone() 的類都應(yīng)該調(diào)用 super.clone() 來(lái)獲得克隆的對(duì)象引用。
該類還必須實(shí)現(xiàn) java.lang.Cloneable 接口,我們要?jiǎng)?chuàng)建其對(duì)象克隆,否則當(dāng)對(duì)該類的對(duì)象調(diào)用克隆方法時(shí),它將拋出 CloneNotSupportedException。
句法:
受保護(hù)的對(duì)象克隆()拋出 CloneNotSupportedException
請(qǐng)注意——在下面的代碼示例中,clone() 方法確實(shí)創(chuàng)建了一個(gè)具有不同 hashCode 值的全新對(duì)象,這意味著它位于單獨(dú)的內(nèi)存位置。但是由于Test對(duì)象c在Test2內(nèi)部,原始類型實(shí)現(xiàn)了深拷貝,但是這個(gè)Test對(duì)象c仍然在t1和t2之間共享。為了克服這個(gè)問(wèn)題,我們顯式地為對(duì)象變量 c 做了一個(gè)深拷貝,這將在后面討論。
// A Java program to demonstrate
// shallow copy using clone()
import java.util.ArrayList;
// An object reference of this class is
// contained by Test2
class Test {
int x, y;
}
// Contains a reference of Test and
// implements clone with shallow copy.
class Test2 implements Cloneable {
int a;
int b;
Test c = new Test();
public Object clone() throws CloneNotSupportedException
{
return super.clone();
}
}
// Driver class
public class Main {
public static void main(String args[])
throws CloneNotSupportedException
{
Test2 t1 = new Test2();
t1.a = 10;
t1.b = 20;
t1.c.x = 30;
t1.c.y = 40;
Test2 t2 = (Test2)t1.clone();
// Creating a copy of object t1
// and passing it to t2
t2.a = 100;
// Change in primitive type of t2 will
// not be reflected in t1 field
t2.c.x = 300;
// Change in object type field will be
// reflected in both t2 and t1(shallow copy)
System.out.println(t1.a + " " + t1.b + " " + t1.c.x + " " + t1.c.y);
System.out.println(t2.a + " " + t2.b + " " + t2.c.x
+ " " + t2.c.y);
}
}
輸出
10 20 300 40
100 20 300 40
在上面的示例中,t1.clone 返回對(duì)象 t1 的淺表副本。要獲得對(duì)象的深層副本,必須在獲得副本后在克隆方法中進(jìn)行某些修改。
淺拷貝是復(fù)制對(duì)象的方法,在克隆中默認(rèn)遵循。在此方法中,舊對(duì)象 X 的字段被復(fù)制到新對(duì)象 Y。在復(fù)制對(duì)象類型字段時(shí),引用被復(fù)制到 Y,即對(duì)象 Y 將指向與 X 所指出的相同位置。如果字段值是原始類型,它復(fù)制原始類型的值。
因此,對(duì)對(duì)象 X 或 Y 中的引用對(duì)象所做的任何更改都將反映在其他對(duì)象中。
淺拷貝便宜且制作簡(jiǎn)單。在上面的示例中,我們創(chuàng)建了對(duì)象的淺表副本。
如果我們想創(chuàng)建對(duì)象 X 的深層副本并將其放置在新對(duì)象 Y 中,則會(huì)創(chuàng)建任何引用對(duì)象字段的新副本,并將這些引用放置在對(duì)象 Y 中。這意味著在對(duì)象中引用的對(duì)象字段中所做的任何更改X 或 Y 將僅反映在該對(duì)象中,而不會(huì)反映在另一個(gè)對(duì)象中。在下面的示例中,我們創(chuàng)建了對(duì)象的深拷貝。
深拷貝復(fù)制所有字段并制作字段指向的動(dòng)態(tài)分配內(nèi)存的副本。當(dāng)一個(gè)對(duì)象與其所引用的對(duì)象一起被復(fù)制時(shí),就會(huì)發(fā)生深拷貝。
// A Java program to demonstrate
// deep copy using clone()
// An object reference of this
// class is contained by Test2
class Test {
int x, y;
}
// Contains a reference of Test and
// implements clone with deep copy.
class Test2 implements Cloneable {
int a, b;
Test c = new Test();
public Object clone() throws CloneNotSupportedException
{
// Assign the shallow copy to
// new reference variable t
Test2 t = (Test2)super.clone();
// Creating a deep copy for c
t.c = new Test();
t.c.x = c.x;
t.c.y = c.y;
// Create a new object for the field c
// and assign it to shallow copy obtained,
// to make it a deep copy
return t;
}
}
public class Main {
public static void main(String args[])
throws CloneNotSupportedException
{
Test2 t1 = new Test2();
t1.a = 10;
t1.b = 20;
t1.c.x = 30;
t1.c.y = 40;
Test2 t3 = (Test2)t1.clone();
t3.a = 100;
// Change in primitive type of t2 will
// not be reflected in t1 field
t3.c.x = 300;
// Change in object type field of t2 will
// not be reflected in t1(deep copy)
System.out.println(t1.a + " " + t1.b + " " + t1.c.x
+ " " + t1.c.y);
System.out.println(t3.a + " " + t3.b + " " + t3.c.x
+ " " + t3.c.y);
}
}
輸出
10 20 30 40
100 20 300 40
在上面的示例中,我們可以看到已經(jīng)為 Test 類分配了一個(gè)新對(duì)象來(lái)復(fù)制一個(gè)對(duì)象,該對(duì)象將返回給 clone 方法。因此,t3 將獲得對(duì)象 t1 的深拷貝。因此,t3 對(duì)“c”對(duì)象字段所做的任何更改都不會(huì)反映在 t1 中。
如果我們使用賦值運(yùn)算符將一個(gè)對(duì)象引用賦給另一個(gè)引用變量,那么它將指向舊對(duì)象的相同地址位置,并且不會(huì)創(chuàng)建該對(duì)象的新副本。因此,引用變量中的任何更改都將反映在原始對(duì)象中。
如果我們使用復(fù)制構(gòu)造函數(shù),那么我們必須顯式復(fù)制所有數(shù)據(jù),即我們必須顯式地在構(gòu)造函數(shù)中重新分配類的所有字段。但是在克隆方法中,創(chuàng)建新副本的工作是由方法本身完成的。所以為了避免額外的處理,我們使用對(duì)象克隆。
相關(guān)閱讀
0基礎(chǔ) 0學(xué)費(fèi) 15天面授
有基礎(chǔ) 直達(dá)就業(yè)
業(yè)余時(shí)間 高薪轉(zhuǎn)行
工作1~3年,加薪神器
工作3~5年,晉升架構(gòu)
提交申請(qǐng)后,顧問(wèn)老師會(huì)電話與您溝通安排學(xué)習(xí)
初級(jí) 202925
初級(jí) 203221
初級(jí) 202629
初級(jí) 203743