[#0x003F] Java的值传递

  所谓的值传递(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() 的区别】,我们是否可以认为:【== 比较的是栈上的两个值是否相等】?

你可能感兴趣的:(java)