先敲下代码做2个实验
下面用例让List添加的元素全为不同的引用地址:
@Test
public void testForList() {
List<Peopel> peopelsForAdd = new ArrayList<>();
peopelsForAdd.add(new Peopel(12, "莉莉"));
peopelsForAdd.add(new Peopel(13, "小明"));
peopelsForAdd.add(new Peopel(14, "李白"));
List<Peopel> peopels = new ArrayList<>();
Peopel peopel;
for (Peopel peopelForAdd : peopelsForAdd) {
// 每次都会为peopel分配一个新内存,即peopel的引用地址每次都是不一样的
peopel = new Peopel();
// 更新当前people的内部信息
peopel.setAge(peopelForAdd.getAge());
peopel.setName(peopelForAdd.getName());
// 将引用地址每次都不同的peopel存入List
peopels.add(peopel);
}
for (Peopel peopelForPrint : peopels) {
System.out.println(peopelForPrint);
}
}
打印的结果显示,列表中的每个元素的信息都是不一样的:
接下来让List添加的元素全为相同的引用地址:
@Test
public void testForList() {
List<Peopel> peopelListForAdd = new ArrayList<>();
peopelListForAdd.add(new Peopel(12, "莉莉"));
peopelListForAdd.add(new Peopel(13, "小明"));
peopelListForAdd.add(new Peopel(14, "李白"));
List<Peopel> peopels = new ArrayList<>();
Peopel peopel = new Peopel();
for (Peopel peopelForAdd : peopelListForAdd) {
// 更新people的内部内容
peopel.setAge(peopelForAdd.getAge());
peopel.setName(peopelForAdd.getName());
// 将引用地址不变的peopel存入List
peopels.add(peopel);
}
for (Peopel peopelForPrint : peopels) {
System.out.println(peopelForPrint);
}
}
打印的结果显示,列表中的每个元素的信息都是一样的:
我们来看看ArrayList类中的源码
以ArrayList类为例,我们观察它的add()方法,可以发现ArrayList用来存放数据的数据结构其实就是一个数组:
这个数组的类型是Object类型的,这说明所有类型的元素都可以被存放到这个数组中。
我们再来看看这段代码,注意标红的这两个地方~
在Java中,如果是对象,那么实际上传递的就都是这个对象的引用地址,所以对于方法add(E e),形参所传的值其实就是对象在堆中的引用地址。
执行elementData[size++] = e,会把这个数组中size++下标所在的位置存入形参e对应的引用地址。
所以看到这里我们可以知道,List类的add()方法其实是为数组存放上每个形参所传递的引用地址。
总结
AarrayList的add()方法每次add新元素其实都是直接把形参对应的引用地址存入到数组中。
所以我们在开发的时候就要格外的认识到这一点。
因为,当我们调用add方法每次都传入相同引用地址的形参时,就会导致:只要改变List中某个元素的内部内容时,就会导致数组中其它下标的元素的内容也跟着一起被改变,这是因为,此时这个数组中的每个元素,实际上指向的都是堆中同一个对象。