应几个法国巴西和印度小程序员的邀请,
我每周在Facebook上发布Java系列小文章,与你们也共享一下。
翻译 | 王楷涵
Java总是传递的参数值。这也就意味着Java复制变量的值,并且将这个值赋值给参数,当参数的值被修改时,原始变量的值将不会被改变。
我们可以通过以下例子来证明这一点:
public static void increment(int i) {
System.out.println("input parameter : " + i);
i++;
System.out.println("parameter chnged to : " + i);
}
public static void main(String[] args){
int v1 = 200;
System.out.println("original var : " + v1);
increment(v1);
System.out.println("original var : " + v1);
}
输出结果为:
original var : 200
input parameter : 200
parameter chnged to : 201
original var : 200
我们可以看到,对于最初始的变量,其值并未发生改变。其原因在于,当Java通过方法increment(int i)传递变量v1时,首先会复制其值(200)给参数i,因此,在自增时,该操作仅仅只会应用在参数i上。初始变量v1则从未触及。
现在我们来看看程序中内存的变化:
首先Java为变量v1分配了内存(0x00EA38FA)且赋值:
address | value | |
---|---|---|
v1 | 0x00EA38FA | 200 |
当调用方法increment()时,Java分配另一块内存地址(0x302C09E0)给变量i并且设置变量i的值为200:
address | value | |
---|---|---|
v1 | 0x00EA38FA | 200 |
... | ... | ... |
i | 0x302C09E0 | 200 |
++操作将改变i的值,示意如下:
address | value | |
---|---|---|
v1 | 0x00EA38FA | 200 |
... | ... | ... |
i | 0x302C09E0 | 201 |
当程序退出increment()方法时, 内存映射为:
address | value | |
---|---|---|
v1 | 0x00EA38FA | 200 |
... | ... | ... |
可以看到v1的值依旧是200。
现在,又出现了另外一个问题:当你传递一个对象引用到方法时,将会发生什么?
在这里给出另外一个例子:
首先我们创建一个只包含一个整数域的INT类:
public class INT {
int i;
INT(int i) {
this.i = i;
}
void increment() {
i++;
}
}
现在我们修改一下程序:
public static void increment(INT I) {
System.out.println("input parameter : " + I + ", value : " + I.i);
I = new INT(201);
System.out.println("parameter changed to : " + I+ ", value : " + I.i);
}
public static void main(String[] args){
INT v1 = new INT(200);
System.out.println("original var : " + v1+ ", value : " + v1.i);
increment(v1);
System.out.println("original var : " + v1+ ", value : " + v1.i);
}
程序输出结果为:
original var : com.test.INT@15db9742, value : 200
input parameter : com.test.INT@15db9742, value : 200
parameter changed to : com.test.INT@6d06d69c, value : 201
original var : com.test.INT@15db9742, value : 200
值不应该被改变吗?当然不,这就是原因所在:
Java首先创建一个v1对象,且赋值为200,请注意v1是一个对象引用,该引用也将被分配内存地址,并且其对象实例也会被分配内存,这是内存映射关系:
address | value | |
---|---|---|
v1 | 0x003065D0 | 0x480012E0 |
... | ... | ... |
instance | 0x480012E0 | 200 |
当调用increment(v1)方法时,Java分配了另外一块内存地址(0x302C09E0)给参数I且将v1的值(0x480012E0)赋值给I:
address | value | |
---|---|---|
v1 | 0x003065D0 | 0x480012E0 |
... | ... | ... |
I | 0x302C09E0 | 0x480012E0 |
... | ... | ... |
instance | 0x480012E0 | 200 |
当执行increment(INT I)方法体中的I = new INT(201);这一行语句时,Java创建了一个新的INT对象实例并且分配地址(0x52001230)给I,以下是内存映射关系:
address | value | |
---|---|---|
v1 | 0x003065D0 | 0x480012E0 |
... | ... | ... |
I | 0x302C09E0 | 0x480012E0 |
... | ... | ... |
instance | 0x480012E0 | 200 |
... | ... | ... |
instance2 | 0x52001230 | 201 |
目前在内存中,存在两个对象引用和两个对象实例了。
在退出increment(INT I)方法后,内存将被回收:
address | value | |
---|---|---|
v1 | 0x003065D0 | 0x480012E0 |
... | ... | ... |
instance | 0x480012E0 | 200 |
这也就是为什么v1引用实例后值保持200不变的原因。
虽然对象引用的参数值不能被修改,但可以更改引用的对象/实例的内容。
例如:
public static void increment(INT I) {
System.out.println("input parameter : " + I + ", value : " + I.i);
I.increment();
System.out.println("parameter changed to : " + I+ ", value : " + I.i);
}
public static void main(String[] args){
INT v1 = new INT(200);
System.out.println("original var : " + v1+ ", value : " + v1.i);
increment(v1);
System.out.println("original var : " + v1+ ", value : " + v1.i);
}
结果输出为:
original var : com.test.INT@15db9742, value : 200
input parameter : com.test.INT@15db9742, value : 200
parameter changed to : com.test.INT@15db9742, value : 201
original var : com.test.INT@15db9742, value : 201
这是因为变量v1和参数都引用同一个对象。I.increment()语句改变了引用实例的内容,结果展示如下:
address | value | |
---|---|---|
v1 | 0x003065D0 | 0x480012E0 |
... | ... | ... |
I | 0x302C09E0 | 0x480012E0 |
... | ... | ... |
instance | 0x480012E0 | 201 |