最近在寫代碼的時候發現一個問題:我定義了兩個List集合A,B,先對第一個集合A add數據進去,接着直接將A的數據賦值給B,結果後面我將集合A給clear掉,然後使用B集合的數據,結果報了數組越界異常。
最後發現集合B的數據也不存在了。測試代碼如下:
List<String> A =new ArrayList<>();
List<String> B =new ArrayList<>();
A.add("111");
A.add("222");
A.add("333");
B=A;
System.out.println("A:"+A);
System.out.println("B:"+B);
A.clear();
System.out.println("A:"+A);
System.out.println("B:"+B);
輸出結果爲:
A:[111, 222, 333]
B:[111, 222, 333]
A:[]
B:[]
看到這個結果我納悶了好久,我明明沒有將集合B的數據給刪掉,只刪除了A的數據,爲什麼B的數據也沒了。於是我猜測:
在用B=A 這個方式對集合B進行復制時將集合A對應的地址賦給了集合B,並不是單純的將集合A的數據賦給集合B。在對A進行clear操作時將A對應的存儲空間也就是地址給刪除了,那麼存儲空間裏的數據也就隨之刪除了。而B指向的地址又是A對應的地址,那麼B裏面的數據也就沒有了。
爲了印證我的猜測,我查找相關資料後得知:
Java中 "="的作用有兩個:
1.賦值
2.指向地址
那麼這兩個作用具體什麼情況下起什麼作用呢?答案如下:
當對基本數據類型進行賦值時 "="的作用就是單純的賦值,例如:int i=1,int j=2;
而當對引用數據類型進行賦值時"="的作用就是將被賦值對象的地址指向賦值對象的地址,例如:
List<String> A =new ArrayList<>(); List<String> B =new ArrayList<>(); A=B;
這裏插入講一下Java堆、棧、常量池的區別:
棧:一些基本類型的變量和對象的引用變量都是在函數的棧內存中分配,但對象本身不存放在棧中,而是存放在堆(new出來的對象)或者常量池中(對象可能在常量池裏)(字符串常量對象存放在常量池中。)
堆:存放new 出來的對象。
常量池:存放字符串常量和基本類型常量(public static final)。對於棧和常量池中的對象可以共享,對於堆中的對象不可以共享。
注意看上面對棧的描述,當我們定義兩個List集合A,B的時候,集合A,B是引用數據類型,那麼他們的引用先是在棧內存中分配,而對象本身是在進行new操作的時候存放在堆內存中,由堆進行內存分配。所以我們用B=A這種方式對集合B進行賦值時其實是將A的引用賦值給B,也就是說B指向了A的地址。當對A或是對B進行數據clear,add等操作時相應的B或A中的數據也會發生對應的變化。
最後,如果要將一個List集合賦值給另一個List集合,並且操作其中一個集合不會影響另一個集合時,可以用以下幾種方法:
//方法一
ArrayList B = new ArrayList<> (A);
//方法二
B.addAll(A);
//方法三
B = A.clone();
//方法四
for(String s: A)
B.add(s);
此次出現的問題糾結了我好久,其實也都是很基礎的問題,但平時不怎麼注意,導致浪費大量時間找尋bug。所以一定要注重基礎啊。
特在此記錄以下!