所谓的值传递(pass-by-value)指的是:传递给方法的是参数值的一个copy。Java方法使用的永远是值传递(很多地方说到的“引用传递”其实也可以看做是值传递,概念搞多了反而还不好理解,干脆说死一点,Java就只使用值传递)。
通过例子来看:
public class Test { public static void change(String s) { s = "changed"; } public static void main(String[] args) { String s = "original"; change(s); System.out.println(s); // Output: original } }
值传递的示意图如下:
由于String是immutable class,即:String对象一旦创建,就不可修改。所以:
String s = "original"; s = "changed";
应该看成:
String s = new String("original"); s = new String("changed");
即s = "changed"并不是把"original"对象修改成"changed",而是新建了一个"changed"对象,而且于此同时"original"对象依然存在。亦即不应该看成下面这种表示:
String s = new String("original"); s.setValue("changed");
正因为String是个不可变类,所以在change()方法中,参数s指向"original",而s的拷贝指向"changed",s的拷贝的行为对s没有影响,所以System.out.println(s)还是打印出"original"。
下面是一个StringBuffer的例子:
public class Test { public static void change(StringBuffer sb) { sb.replace(0, sb.length(), "changed"); } public static void main(String[] args) { StringBuffer sb = new StringBuffer("original"); change(sb); System.out.println(sb.toString()); // Output: changed } }
sb.replace(0, sb.length(), "changed"),或者更准确地说应该是copyOfSb.replace(0, sb.length(), "changed")并没有创建新的对象,而是修改了原有的对象,所以System.out.println(sb.toString())打印出来的是changed。
题外话:一般说来,String和基本类型都是immutable class,而StringBuffer、Date还有数组都是可变类。
2010-7-6补充:用final来修饰方法的参数可以强制禁止参数的拷贝指向新的对象,例如:
public class Test { public static void change(final String s) { s = "changed"; // 非法!!! } public static void main(String[] args) { String s = "original"; change(s); System.out.println(s); } }
这里final就可以禁止copy of String s指向新对象"changed"的行为:
2011.10.23 补充:
对参数来说,可以分三类:(1)基本类型 primitive type;(2)可变类对象reference;(3)不可变类对象reference。(2)(3)的情况上面已经讨论过了,那么基本类型的情况如何呢?
基本类型的情况有一点特殊。基本类型和reference都是在栈上的值,只是reference存的是堆上对象的地址值,而基本类型存的是本身的值。如下图所示:
当 (i,j) 作为参数传入时,j会被原样copy一份,这份copy仍然指向Integer(10);i也会被原样copy一份,值仍然为5。如下图所示:
所以此时在方法里改变 (i,j) 的值,其实对 (i, j) 并没有什么影响:
当方法执行完毕,堆栈内容和方法执行前没有变化。
可以总结一下,java的值传递是在栈上进行的,即将栈上的primitive type或是reference copy一份,再传递给方法,方法实际操作的是这份copy。这份copy能否影响到堆上的值,要看方法具体对copy的操作是啥,如果是setter,那肯定改变了堆置,如果是new,其实不会改变。
2011.10.26 补充:
联系到【== 与 Object.equals() 的区别】,我们是否可以认为:【== 比较的是栈上的两个值是否相等】?