一个Java对象引用问题
遇到问题
在做项目的时候,遇到一个很奇葩的问题,导致卡了半天。其实就是个简单的Java对象引用问题,但是印象很深刻。
这里需求是这样的:一个循环,把得到的一些值放到对象里,循环添加到一个List中。
简单例子
下面写个简单的例子,里面对问题进行了简化。
首先是一个简单的Java对象:
public class Person{
private String name;
private String age;
//get和set方法省略不写了,或者你用Lombok也可以
}
看以下两段代码:
错误
Person p = new Person();
List<Person> list = new ArrayList<Person>();
for(int i = 0; i<3; i++){
if(i == 0){
p.setName("ZhangShan");
p.setAge(20);
list.add(p);
}else if(i==1){
p.setName("LiSi");
p.setAge(21);
list.add(p);
}else if (i==2){
p.setName("WangWu");
p.setAge(22);
list.add(p);
}
}
System.out.println(list);
这个是第一段,是有错误的。
最后输出的list内会是3个一模一样的对象。这就是因为对象引用的问题。
因为这里对象只是在循环外面声明了一次,循环内操作的所有的p都是指向同一个内存地址的。这点和C或者C++里面的指针相似。
可以简单理解为,在地址为0的内存块处申请了一个地址M0,然后把p对象的数据全存在M0内。
三次循环之后,list中存的就是[M0,M0,M0]
。
每次p.setValue的时候改变值都是改变的M0这个内存块中的值。
所以说最后这样只会留下最后一次的值,也就是结果会输出[{"WangWu",22}, {"WangWu",22}, {"WangWu", 22}]
。
画个简单的图示意一下:
改正
进行修改:
List<Person> list = new ArrayList<Person>();
for(int i = 0; i<3; i++){
//这里的new对象改变了位置,放到了循环内。
Person p = new Person();
if(i == 0){
p.setName("ZhangShan");
p.setAge(20)
list.add(p);
}else if(i==1){
p.setName("LiSi");
p.setAge(21);
list.add(p);
}else if (i==2){
p.setName("WangWu");
p.setAge(22);
list.add(p);
}
}
sout(list);
这样修改了之后,最大的区别是把new放在了循环内。
这时候会在每次循环的时候都new一个新的对象,也就是申请一个新的内存块来存现在的值。
这时候每次循环中的p都是一个新的p了,其实应该说是p0,p1,p2这样来称呼。
总结
其实可以用临时对象,重写一下构造方法,然后list.add(new Person("ZhangSan",20))
这样比较规范。
总的来说还是不够重视基础,太长时间不用忘了都。或者说代码不规范不干净。
有诗曰:
代码千万行,规范第一条。
编程不规范,调试两行泪。