Java 的值传递

Yao Yao on July 4, 2010

所谓的值传递 (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"; // String s = new String("original");
s = "changed"; // 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  
	}  
} 

题外话:一般说来,String 和 primitive 都是 immutable class,而 StringBufferDate 还有数组都是可变类。

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 = xxx 或者 j = new Integer(xxx),其实对 (i, j) 并没有什么影响:当方法执行完毕,堆栈内容和方法执行前没有变化。

可以总结一下,java 的值传递是在栈上进行的,即将栈上的 primitive type 或是 reference copy 一份,再传递给方法,方法实际操作的是这份 copy。这份 copy 能否影响到堆上的值,要看方法具体对 copy 的操作是啥:如果是 setter,那肯定改变了堆值;如果是 new,其实不会改变。

2011.10.26补充:

联系到== 与 Object.equals() 的区别,我们是否可以认为:== 比较的是栈上的两个值是否相等



blog comments powered by Disqus