一个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))这样比较规范。

总的来说还是不够重视基础,太长时间不用忘了都。或者说代码不规范不干净。

有诗曰:
代码千万行,规范第一条。
编程不规范,调试两行泪。