拷贝构造器问题:Robot类对象操作后状态异常原因咨询
问题分析:Java对象引用与浅拷贝导致的行为差异
咱们先把你遇到的问题拆解清楚,这本质是Java里引用类型传递和浅拷贝的经典坑,分两部分给你解释明白:
一、三个Robot对象的内存指向关系
先明确你创建三个对象时的内存逻辑:
Robot terminator = new Robot(0,0,0,0,1):在堆内存中创建了一个全新的Robot实例,terminator变量是指向这个实例的引用。Robot b = terminator:没有创建新对象,只是给同一个堆内存实例新增了一个引用别名。b和terminator完全指向同一个Robot实例。Robot a = new Robot(terminator):调用拷贝构造器创建了一个新的Robot实例,但拷贝构造器里的location = copy.location是把原对象的location数组引用直接赋值过来——这就是浅拷贝,新实例的location和原实例的location指向同一个数组对象。
另外提个小bug:你的主构造器里有局部变量覆盖问题,会导致类的location属性初始化逻辑错误:
public Robot (int east, int north, int west, int south, int direction) { this.direction = direction%4 ; location = new int[4] ; int[] location = {east,north,west,south} ; // 这里是局部变量,类的location属性还是上面的空数组! }
应该改成this.location = new int[]{east,north,west,south},才能正确初始化类的location属性。
二、move()方法影响所有对象的原因
move()方法的逻辑是location[direction]++:
location是数组,属于引用类型,terminator、a、b的location最终都指向堆内存里的同一个数组对象(b和terminator是同一个Robot实例,a是新实例但共享了数组引用)。- 不管哪个对象调用
move(),本质都是修改同一个数组里的元素,所以所有对象的location都会同步变化。
三、setDirection()行为差异的原因
direction是基本类型int,和引用类型的数组完全不同:
- 拷贝构造器里的
direction = copy.direction是值传递:把原对象的direction数值复制给新实例a的direction变量,a的direction是独立的内存空间,和原对象无关。 - 当调用
terminator.setDirection(2)时:terminator和b是同一个实例,修改的是同一个direction变量,所以b的direction跟着变;而a是独立实例,它的direction不受影响。 - 当调用
a.setDirection(3)时:只是修改a自己的direction变量,terminator和b是另一个独立实例,自然不会被影响。
结合你的输出验证
从输出结果完全符合上述逻辑:
初始状态:三个对象的Location都是[0,0,0,0],Direction都是N
操作后:
- terminator:Location[0,0,1,0](move修改了共享数组),Direction W(setDirection(2)的结果)
- a:Location[0,0,1,0](共享数组被修改),Direction S(自己调用setDirection(3)的结果)
- b:Location[0,0,1,0](共享数组被修改),Direction W(和terminator是同一个实例,同步修改)
解决方法
要让a的location独立,需要把拷贝构造器改成深拷贝,对location数组创建新的副本:
Robot( Robot copy ) { direction = copy.direction ; // 深拷贝数组,创建新的数组对象 location = new int[copy.location.length]; System.arraycopy(copy.location, 0, location, 0, copy.location.length); }
这样a的location就是独立的数组,调用move()就不会影响原对象了。
内容的提问来源于stack exchange,提问作者Ufuk Keskin




