2013-06-04 61 views
2

我不完全理解Java何時傳遞一個副本/值以及何時傳遞「引用」(指針)。獲取對象的副本

我想分配一個靜態對象的副本,但我不知道該如何去做。

我有這樣的:

static ArrayList<MyObject> myObjects; 

我想myObjects的副本,這樣我可以用值玩耍,而不會影響原來的。當我使用吸氣劑時,它是否通過參考或複製/值:

public static ArrayList<MyObject> getMyObject() 
{ 
    return ThisClass.myObjects; 
} 

返回什麼?如果它是一個參考,我怎樣才能得到一份副本?

我見過這些:

How do I copy an object in Java?

Java: getter method vs. public instance variable: performance and memory

Is Java "pass-by-reference" or "pass-by-value"?

How can Java assignment be made to point to an object instead of making a copy?

但是我還是不太明白我會回來的。

+0

如果您的問題是關於不修改原始列表中的MyObject,您將需要列表的深層副本。 –

回答

5

Java將永遠只要返回一個參考,而不是副本,因爲它不是一個基本類型(又名longintshort等或原始包裝LongInteger之一,Short

要獲得複製您需要複製數據,使用複製構造函數或使用方法clone這將創建一個具有適當值的新對象

帶有列表的副本構造函數的示例,默認情況下這是一個「淺拷貝「意思是裏面的物體是相同的。

List<MyObject> myNewCopiedList = new ArrayList<MyObject>(oldList); 

對於「深拷貝」,意思是裏面的對象可以在不影響您將需要一個新的列表,然後添加對象的拷貝/克隆,並添加原件突變。

示例,假設MyObject具有複製構造函數或clone方法。

List<MyObject> myNewCopiedList = new ArrayList<MyObject>(); 
for (MyObject myo : oldList){ 
    myNewCopiedList.add(new MyObject(myo)); // if there is a copy constructor 
    myNewCopiedList.add(myo.clone()); // if there is clone method 
} 
+0

添加了關於淺與深複製的信息 – greedybuddha

+0

這是非常好的。我忘記了所有關於深拷貝的問題,並且幾乎遇到了這個問題。感謝您提供的所有信息!在語言之間切換總是一件麻煩事,但這比C更像C。再次,這是驚人的,所以謝謝你! – RileyE

1

從技術上講,Java始終是按值傳遞的。然而,對於初學者的想法,這樣更容易考慮:

如果它是一個原始類型,它是傳遞值。

如果它是一個對象,它是通過引用。

因此,在您的示例中,您返回對ThisClass中同一個static對象的引用。我之所以這麼說是因爲技術上傳遞值是因爲您的變量myObjects實際上存儲了您聲明的ArrayList<MyObject>的內存地址,並且這是通過的。

+0

我認爲將類型變量視爲持有「對象標識符」會好得多。所有參數都按值傳遞;調用'PaintBooth.Spray(myCar)''時持有myCar''汽車@ 14912C'就類似於有人在修理廠寫車輛識別碼上的工單的頂部'myCar'舉行,並提交給油漆車間,然後找到汽車並對其進行油漆。油漆車間沒有車;而是賦予VIN的一份*副本,並使用VIN的該副本來檢索該車。 – supercat

+0

@supercat我喜歡你的比喻,但我不認爲它和我說的有什麼不同。 :)無論你稱之爲「內存地址」還是「對象標識符」,這個概念都是一樣的。 – asteri

+0

當一個變量通過引用傳遞時,接收該變量的方法被禁止持續對其引用。任何對該變量所做的任何事情都必須在它返回之前完成。相比之下,當一個對象標識符被傳遞時,接收者可以自由地以任何它認爲合適的方式複製該標識符,並且在任何時間,甚至在該方法返回給調用者之後,使它被用於任何任意目的。 – supercat

1

爲了正確地使對象的副本,一個必須知道哪些非基本領域封裝對象的狀態

  • 易變的方面,而不是它的身份

  • 的身份一個對象和其他不可改變的方面,但沒有可變的方面。對象的

  • 方面,其預計從不暴露於任何代碼,這可能會發生變異他們(而不是身份)

  • 對象狀態的易變的方面,以及它的身份

基於什麼字段封裝的 Foo

  • 一個正確副本

    如果一個的封裝可變狀態的字段,Foo副本中的對應字段應該持有對具有相同狀態的不同對象的引用。

  • 如果一個字段封裝對象的身份,在副本中那場必須持有到相同對象的引用在Foo - 不是副本

  • 如果字段封裝僅比身份其他不可變方面,則Foo副本可以或者保持相同的對象的引用作爲在Foo,或具有相同的不可變的狀態下,方便的任何對象。

  • 如果一個字段封裝了可變狀態和標識,因爲前兩個需求會衝突,所以不可能單獨複製對象。

在某些情況下,可能會複製一組使用相互引用來封裝狀態和標識的可變對象。這樣的副本必須在整個集合上執行;對於集合中的每個對象,在原始對象中封裝可變狀態和原始集合中另一個對象的身份的任何字段必須在副本中引用複製集合中的對應對象。

+0

這是很棒的信息。謝謝! – RileyE

2

想想這樣。 Java總是傳遞價值。

對於原語,它是通過值(實際值)。 對於對象,它是通過參考值傳遞的。

public int square(int a) { //The parameter a is copy of actual int itself. 
//So now there are 2 ints 
a=a*a; //Only local copy a is actually modified. 
     //The integer variable passed(in caller function) is not modified. 
return a; 
} 

如果調用DoSomething的(d)如d是一個對象,指向引用的副本到這個對象分配給參數a但只有一個對象。

public void doSomething(Object a) { 
// Here the parameter is a reference which points to an 
// object, not the object itself 
a.doMore(); //But doMore() does things using a different ref but on the same object. 
      //The object can be modified! 

Object b = new Object(); 
a = b; //Object referenced by passed parameter does not change but 
     //copy of reference now points to different object. 
     // Now there is no reference of original object passed in this method. 
} 
+0

好的。所以,Java中有真正的指針被傳遞,但它們似乎是「不可見的」/未標記的。現在對我來說更有意義。通過所有這些答案,幫助我思考記憶的重新分配,而不是考慮「複製」。 – RileyE