更新:
在一次搜索“變量聲明在循環體內還是循環體外”問題時,碰見了一個這樣的代碼,與本文類似,代碼如下:
Document [] old ......//這是數據源
EntityDocument[] newArray = new EntityDocument[old.length];//自定義的類,為了把Document里數據保留下來避免Document被關聯對象關閉而導致無法取出數據。
EntityDocument d = new EntityDocument();
for(int i=0;i<old.length;i++){
d.content = old[i].getContent();
d.key = old[i].getKey();
d......................
newArray[i] = d;//如此對象重用.....
}
上面的代碼最終結果會導致newArray數組中的每個元素都等於最后一個元素,原因就是每次向newArray中存儲對象時,沒有新建一個對象進行儲存,從而導致錯誤。
上面代碼只需要將第3行的 EntityDocument d = new EntityDocument(); 放入循環體內即可實現正常功能。
這種錯誤隱藏得比較深,所以要好好記住。
原文
在刷《劍指OFFER》的時候,自己犯了一個錯誤,發現:在鏈表中存儲一個對象時,如果該對象是不斷變化的,則應該創建一個新的對象復制該對象的內容(而不是指向同一個對象),將這個新的對象存儲到鏈表中。如果直接存儲該對象的話,鏈表中的對象也會不斷變化。基本數據類型和String則沒有這種問題。
其實這歸根結底是一個傳值和傳引用的問題:
1.如果存儲到鏈表中的是對象,則存儲的是引用(地址),所以該地址上的內容變化時,會引起對象的變化。
2.如果存到鏈表中的是基本數據類型或者String,存儲的就是該數值,不會再發生變化了。(其實String是對象,存儲的是引用,后面討論)。
舉個例子:
import java.util.ArrayList;
public class Test {
public static class Person {
int age=1;
}
public static void main(String[] args) {
//=======ArrayList存儲String或者基礎數據類型=========
ArrayList<String> list = new ArrayList<>();
String aString="abc";
list.add(aString);
System.out.println("before:"+list.toString());
aString="123";
System.out.println("after:"+list.toString());
//========ArrayList存儲對象=======
ArrayList<Person> pList = new ArrayList<>();
Person a = new Person();
Person b = new Person();
b = a;
Person c = new Person();
c.age=a.age;
pList.add(a);
pList.add(b);
pList.add(c);
System.out.print("before:");
for (Person person : pList) {
System.out.print(person.age+" "); //a,b,c的age此時都是1
}
System.out.println();
a.age=2;
System.out.print("after:");
for (Person person : pList) {
System.out.print(person.age+" "); //輸出:2,2,1
}
//關鍵原因:b是和a指向同一個對象,c不是同一個對象
}
}
before:[abc] after:[abc] before:1 1 1 after:2 2 1
上面的代碼可以知道,存儲Person這個對象時(存儲的是地址),b和a其實是同一個地址,所以a指向的對象改變,會引起鏈表中的前兩個結點(地址相同)改變,而如果要使存進鏈表的person存儲的是a存儲時的狀態,只能新建一個對象c,令c的內容等於a,才在后面不會發生變化(因為該地址指向的內容沒有再發生改變了)。
關於String的討論:其實String也是對象,存儲的其實也是引用(地址),但為什么上面代碼中before和after輸出的內容都是“abc”呢?其實在aString="123";時,相當於aString=new String("123"),即aString指向了另一個對象,aString存儲的地址變成了“123”的地址,但鏈表中存儲的還是“abc”的地址,所以鏈表中的內容不變。
更詳細的傳遞討論:值傳遞和引用傳遞討論
