Java “Pass by value” 解析
我在许多论坛和文章的答复上看到很多人都认为Java是pass by reference的,但我查了些资料,确实Java是pass by value的。如果你已了解java是pass by vlaue而非pass by reference的,那么你就可以pass这篇文章了,看其他你喜欢的文章去吧!如果你不以为Java是pass by vlaue或还坚信Java是pass by reference的,那么就跟着我的思路走一下吧。我会让你了解Java到底是pass by value还是pass by referenve。本人是个菜鸟,只是把自己所学的东西写出来给同级别的菜鸟学习学习。望高手看了多多指教,不要见笑。
Java是使用“pass by value”『函数调用』方式,这常常造成许多人迷惑。在所有地点,Java都严格采用pass by value。让我们来看一个简单的例子:
清单一:
class Param {
public static void main (String[] args) {
int x = 5;
triple(x);
System.out.println ("x after triple: " + x);
}
private static void triple (int arg) {
arg = arg * 3;
System.out.println ("arg in triple: " + arg);
}
}
会产生这样的输出:
arg in triple: 15
x after triple: 5
这段代码只是传入一个int而已,还不至于让人糊涂。但如果参数中传递的是一个对象,就可能把你弄迷糊了。如果我在程序中以Date对象表示日期,那么程序看起来想这样:
清单二:
import java.util.*;
class Param {
public static void main (String[] args) {
Date d1 = new Date ("1 Jan 2004");
nextDateUpdate (d1);
System.out.println ("d1 after nextDay: " + d1);
Date d2 = new Date ("1 Jan 2004");
nextDateReplace (d2);
System.out.println ("d2 agter nextDay: " + d2);
}
private static void nextDateUpdate (Date arg) {
arg.setDate (arg.getDate() + 1);
System.out.println ("arg in nextDay: " + arg);
}
private static void nextDateReplace (Date arg) {
arg = new Date (arg.getYear(),arg.getMonth(),arg.getDate() + 1);
System.out.println ("arg int nextDay: " + arg);
}
}
会产生这样的输出:
arg in nextDay: Jan 02 00:00:00 CST 2004
d1 after nextDay: Jan 02 00:00:00 CST 2004
arg int nextDay: Jan 02 00:00:00 CST 2004
d2 agter nextDay: Jan 01 00:00:00 CST 2004
这个实例可能让你看起来有点迷惑。有人会说如果java是pass by value的话,d1 after nextDay:应该为Jan 01 00:00:00 CST 2004才对!错!Java中的对象是object reference。事实上,nextDateReplace()是在与『Date对象(d2)的reference的复件』打交道,而不是与『Date对象(d2)的复件』打交道。在进入nextDateUpdate(Date arg)之时,这个对象看起来象这样:
聪明的你这下应该明白了吧!如果还没明白!没关系,再来一个更详细的例子:
清单三:
import java.awt.Point;
class Param {
public static void main (String[] args) {
Point p = new Point(0,0); //1
int i = 10;
System.out.println ("Before modifyPoint: " + "p = " + p +
" and i = " + i);
modifyPoint (p,i); //2
System.out.println ("After modifyPoint: " + "p = " + p +
" and i = " + i);
}
private static void modifyPoint (Point pt , int j) {
pt.setLocation (5,5); //3
j = 15;
System.out.println ("During modifyPoint: " + "pt = " + pt +
" and j = " + j);
}
}
会产生这样的输出:
Before modifyPoint: p = java.awt.Point[x=0,y=0] and i = 10
During modifyPoint: pt = java.awt.Point[x=5,y=5] and j = 15
After modifyPoint: p = java.awt.Point[x=5,y=5] and i = 10
这段代码在//1处建立了一个Point对象并设初值为(0,0),接着将其值赋予Object reference变量p。然后对基本型别int I赋予数值10。//2调用static modifyPoint(),传入p和i。modifyPoint()对第一个参数pt调用了setLocation(),将其坐标改为(5,5)。然后将第二个参数j赋值为15。当modifyPoint()返回的时候,main()打印出p和i的值。
根据程序输出,modifyPoint()改变了//1所建立的Point对象,却没有改变int i。在main()之中,i被赋值为10。由于参数通过by value方式传递,所以modifyPoint()收到i的一个副本,然后它将这个副本改为15并返回。main()内的原值i并没有受到影响。
对比之下,你或许认为//1建立的point对象也没有被modifyPoint()修改。毕竟Java是pass by value方式来传递参数。于是乎,当调用modifyPoint()并传入//1所建立的point对象时,就会产生一个复件配合modifyPoint()工作。modifyPoint()之中对于point对象所做的修改不会反映到main()中,因为它们是两个不同的对象嘛。对不对?错!
事实上modifyPoint()是在与是在与『point对象的reference的复件』打交道,而不是与『point对象的复件』打交道。你知道的,p是个object reference,并且Java是pass by vlaue方式传递参数。更明确的说,Java是以by value方式传递object reference。当p从main()被传入modifyPoint()时传递的是p(也就是一个reference)的复件。所以modifyPoint()是在与同一个对象打交道,只不过通过别名pt罢了。在进入modifyPoint()之后和执行//3之前,这个对象看起来象这样:
所以//3执行过后,这个point对象已经改变为(5,5)。
结语:通过上面说的,Java是pass by value方式传递参数的。当然就算你已知道java是pass by value方式传递参数的,它有时也会让你感到迷糊。从本质上说,object reference是按值传递的(pass by value),因此我们能够修改参数对象的内部状态。我们应该采用《重构》中的‘Remove Assignments to Parameters’进行重构,以此来避免可能发生的问题。