在java中关于参数传递时到底是按值传递还是按引用传递一直是比较头疼的。
我的理解是,基本类型是按值传递,而对象类型则是按引用传递的。但是,大家可能会发现一个问题。看下面的程序:
public class Test {
public static void test(String str) {
str = "World";
}
public static void main(String[] args) {
String string = "Hello";
test(string);
System.out.println(string);
}
}
这段程序的输出结果是:Hello,而不是大家所期待的HelloWorld。原因是String类型是一个final型的变量。也就是他的值是不允许被改变的。在main方法中string是一个引用类型的变量时毫无疑问的,把他传给test方法的str,而str也是一个引用变量,此时在栈内存当中会存在两个变量string和str,他们虽然在栈内存当中是放在两个地方,但是却指向堆内存当中的同一个地址。简单的说,他们两个所存储的地址是一样的,都指向常量字符Hello。而在test的方法中,给str重新赋值了,按照常理,此时string引用也会跟着改变,而事实是没有。原因就在于String类型变量的特殊性。String类型的变量时不能被改变的,他尝试给他重新赋值之后,会给他重新分配地址,这就是说str被重新指向了别的地址了,而不是原来的string的地址了。
上面仅仅是个特例,下面我们来看一个基本类型传值的例子:
public static int test(int c) {
c=11;
return c;
}
public static void main(String[] args) {
int a=19;
int b = test(a);
System.out.println(a==b);
}
上面的代码输出了false。也就是说在把a赋值给c的时候,实际上是将a复制了一份放到了堆内存当中,然后让栈内存中的c指向这个新值。所以,此时的a和c将代表不同的内存地址,因此对c的任何变化和运算都不至于会影响到a。那么打印a==b自然也就是false了。
我们再来看另外一段程序:
public static StringBuffer test(StringBuffer str) {
str.append("world");
return str;
}
public static void main(String[] args) {
StringBuffer string = new StringBuffer("hello");
StringBuffer str = test(string);
System.out.print(string==str);System.out.println(string);
}
这段程序和上个程序的区别是使用了StringBuffer对象,这时就是一个按引用传递了。即,当把string赋值给str的时候,并没有在堆内存当中再创建一个string的复制对象,而是简单的把 string的地址赋给str,所以此时string和str会指向同一个堆内存地址,所以,对任何一个的修改都会影响到另一个。就好比,两个人合起来花一百块钱,谁多花了都会影响到另一个人,因为是共有的。由此可以猜到改程序的打印结果:true helloworld。
现在就有一个问题,如果有两个基本类型的变量,int a 和 int b,如何通过一个方法将他们的顺序进行交换?答案好像是不行。如果你把a和b传递给某个方法作为参数,那么会在堆内存当中复制一个a和b,也就是新的a和b的任何修改不会影响到原来的。所以,交换两个变量的顺序就只能在方法内部有效。