最近在写代码的时候发现一个问题:我定义了两个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。所以一定要注重基础啊。
特在此记录以下!