坑
case1
case1:
@Data
class Person {
Integer age;
String name;
public Person(Integer age, String name) {
this.age = age;
this.name = name;
}
}
List<Person> v1List = new ArrayList<>();
v1List.add(new Person(11, "小刚"));
v1List.add(new Person(12, "小红"));
List<Person> v2List = new ArrayList<>();
v2List.addAll(v1List);
v2List.forEach(person -> { // 改变v2List的每个元素的年龄为0
person.setAge(0);
});
logger.info("v1List: {}", v1List);
v1List 输出为[{age:0, name: “小刚”}, {age:0, name: “小红”}]
验证:
问:为什么修改v2List的元素会影响到原列表v1List?
答:因为v1List中装的是对象,这里的对象相当于是引用,即指针,指向的是内存中真正存储的这个对象的地址。
所以当我们将v1List中所有对象就赋值给v2List的时候,相当于把引用或者指针复制了一份到v2List中,修改v2List的元素相当于修改v1List中的对应元素,因为他们的引用相同都指向同一个内存地址 可以参考上面这幅图的框住的部分,可以看到v1List的第一个元素的内存地址和v2List的对应元素的内存地址相同,第二个元素同样如此,所以当我这样修改v2List的时候,v1中的元素也会变,大家一定要注意这个坑。
case2
case2:
List<Integer> v1List = new ArrayList<>();
v1List.add(1);
v1List.add(2);
List<Integer> v2List = new ArrayList<>();
v2List.addAll(v1List);
v2List.set(0, 111); // 改变v2List的第一个元素的值
logger.info("v1List: {}", v1List);
v1List输出为1, 2
下面如果把list中存储的元素设置为基本数据类型,则不会产生错误。
问:为什么List中存放基本数据类型数据的时候运行上面这个程序不会产生case1的bug呢?int,long和float是基本数据类型,但是对应的包装类Integer,Long, Float还是属于引用类型的啊,为什么的不会产生错误呢
答:?
case3
如果一个List里装的是字符串对象,则也不会产生case1中碰到的错误
case3:
List<String> strs1 = new ArrayList<>(); // line 1
strs1.add("a"); // line 2
strs1.add("b"); // line 3
List<String> strs2 = new ArrayList<>(); // line 4
strs2.addAll(strs1); // line 5
strs2.set(0, "aaa"); // 改变strs2的第一个元素的值, line 6
logger.info("strs1: {}", strs1); //line 7
输出为a,b
问:为什么List中存放字符串时也不会产生bug呢?
答:这是因为在java中把所有的不同的字符串都放到了字符串常量池中,一旦产生了一个string是常量池中没有的,就会将其加入到池子中,然后将新的对象的引用指向这个新常量。 以case3为例,执行到line 4时,常量池中有"a"和"b",当执行万line 6时,池子中有"a"和"b"和"aaa"(因为aaa是新的,所以会加入池子中), 此时strs1.get(0)和get(1)指向池子中的"a"和“b“,strs2的get(0)指向"aaa", get(1)指向不变。