复制构造函数的一个问题
An Issue of Copy Constructor
我创建了一个 class 机器人,它具有两个属性:方向(它只是一个字符串 E、A、S、N)和位置(一个包含 4 个整数的数组,用于在两个方向上保持位置). class 有主要的构造函数:
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} ; }
还有一个拷贝构造函数:
Robot( Robot copy ) {
direction = copy.direction ;
location = copy.location ;
}
它也有方法,但我想展示这两个方法:move() 和 setDirection()。
public void move() {
location[direction]++ ;
}
public void setDirection( int direction ) {
this.direction = direction ;
}
我创建了一个 Robot 对象并将其等同于一个新对象,还通过复制构造函数使用了另一个新对象:
Robot terminator = new Robot(0,0,0,0,1) ;
Robot b = terminator ;
Robot a = new Robot(terminator) ;
问题来了,当我对终止符 a 或 b 使用 move() 方法时,其他位置都发生了变化,但是当我对终止符使用 setDirection 时,a 没有改变,但 b 发生了变化。此外,当我对 a 使用相同时,b 的 none 和终结符的方向会发生变化。那么区别是什么呢?为什么在终结器上实现的 move() 方法会影响副本,但在使用 setDirection() 时却不会?
System.out.println(terminator) ;
System.out.println(a) ;
System.out.println(b) ;
terminator.setDirection(2);
a.setDirection(3) ;
terminator.move() ;
System.out.println(terminator) ;
System.out.println(a) ;
System.out.println(b) ;
输出:
Location[0, 0, 0, 0]Direction N
Location[0, 0, 0, 0]Direction N
Location[0, 0, 0, 0]Direction N
Location[0, 0, 1, 0]Direction W
Location[0, 0, 1, 0]Direction S
Location[0, 0, 1, 0]Direction W
两个 Robot
实例都持有对同一位置 int[]
的引用。您需要在创建新的 Robot
实例时创建一个新数组,例如,使用 Arrays.copyOf
:
Robot( Robot copy ) {
direction = copy.direction ;
location = Arrays.copy(copy.location, copy.location.length);
// Here ---^
}
看看你的构造函数:
Robot( Robot copy ) {
direction = copy.direction ;
location = copy.location ;
}
你在这里做的是浅拷贝。
您的 direction
是原始类型,因此您复制的 Robot
获得原始 Robot
方向值的副本。他们对 direction
变量
的引用不同
同时,您的 location
是一个数组,因此您复制的 Robot
获得了对原始 Robot
位置的引用。 Robot
都具有相同的 location
数组。改变一个会影响另一个。
如果你想分开两个Robot
实例,你必须做一个deep copy
数组location
。
而不是:
location = copy.location;
做:
location = Arrays.copyOf(copy.location, copy.location.length);
我创建了一个 class 机器人,它具有两个属性:方向(它只是一个字符串 E、A、S、N)和位置(一个包含 4 个整数的数组,用于在两个方向上保持位置). class 有主要的构造函数:
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} ; }
还有一个拷贝构造函数:
Robot( Robot copy ) {
direction = copy.direction ;
location = copy.location ;
}
它也有方法,但我想展示这两个方法:move() 和 setDirection()。
public void move() {
location[direction]++ ;
}
public void setDirection( int direction ) {
this.direction = direction ;
}
我创建了一个 Robot 对象并将其等同于一个新对象,还通过复制构造函数使用了另一个新对象:
Robot terminator = new Robot(0,0,0,0,1) ;
Robot b = terminator ;
Robot a = new Robot(terminator) ;
问题来了,当我对终止符 a 或 b 使用 move() 方法时,其他位置都发生了变化,但是当我对终止符使用 setDirection 时,a 没有改变,但 b 发生了变化。此外,当我对 a 使用相同时,b 的 none 和终结符的方向会发生变化。那么区别是什么呢?为什么在终结器上实现的 move() 方法会影响副本,但在使用 setDirection() 时却不会?
System.out.println(terminator) ;
System.out.println(a) ;
System.out.println(b) ;
terminator.setDirection(2);
a.setDirection(3) ;
terminator.move() ;
System.out.println(terminator) ;
System.out.println(a) ;
System.out.println(b) ;
输出:
Location[0, 0, 0, 0]Direction N
Location[0, 0, 0, 0]Direction N
Location[0, 0, 0, 0]Direction N
Location[0, 0, 1, 0]Direction W
Location[0, 0, 1, 0]Direction S
Location[0, 0, 1, 0]Direction W
两个 Robot
实例都持有对同一位置 int[]
的引用。您需要在创建新的 Robot
实例时创建一个新数组,例如,使用 Arrays.copyOf
:
Robot( Robot copy ) {
direction = copy.direction ;
location = Arrays.copy(copy.location, copy.location.length);
// Here ---^
}
看看你的构造函数:
Robot( Robot copy ) {
direction = copy.direction ;
location = copy.location ;
}
你在这里做的是浅拷贝。
您的 direction
是原始类型,因此您复制的 Robot
获得原始 Robot
方向值的副本。他们对 direction
变量
同时,您的 location
是一个数组,因此您复制的 Robot
获得了对原始 Robot
位置的引用。 Robot
都具有相同的 location
数组。改变一个会影响另一个。
如果你想分开两个Robot
实例,你必须做一个deep copy
数组location
。
而不是:
location = copy.location;
做:
location = Arrays.copyOf(copy.location, copy.location.length);