今晚在某QQ群上看见有讨论该问题,于是想做个MARK。
一般性:
大家都知道,在JAVA中变量有以下两种:
基本类型变量,包括char、byte、short、int、long、float、double、boolean。
引用类型变量,包括类、接口、数组(基本类型数组和对象数组)。
对于基本类型的变量:
值传递,在调用方法时传给方法一个参数的拷贝值(这个值就是value),所以方法体内部使用的参数只是一个“附件”。它与原参数没有关系,在方法体内部中,该值的改变也不会影响原参数。
对于引用(这里改称指针更形象)类型的变量:
引用传递,在调用方法时传给方法一个参数引用(这个值就是指针地址)的拷贝,(所以也有人支持这里也是值传递,传递的是地址值)。可以打个比喻:我熊猫有一张藏宝图指向一堆宝藏(指针指向对象),我派佣人去取宝藏(调用方法),我就复印了一份藏宝图交给佣人(拷贝指针作为参数传递给被调用的方法)。佣人根据藏宝图找到宝藏并变卖了一部份宝藏(在方法内部通过拷贝的指针改变了对象的值)。现在我手上的这份藏宝图指向的宝藏的数量是否被改变了?所以,引用传递是会改变原值的。
特殊性:
问:String 是值传递还是引用传递? 为什么值不会改变?那String[]呢?StringBuffer呢?
答:String是"类似值传递"、String数组是引用传递。
public class TestString{
public static void main(String args[]){
String strTest=new String("ab");
//String strTest="ab";这两种形式的定义,输出的都是ab
setString(strTest);
System.out.println(strTest);
}
private static void setString(String str){
str=str+"c";
}
}
解析:对于java初学者来说,很多人在执行程序以前一定认为输出的将会是"abc”,但是结果告诉我们输出的会是"ab",这就是String的“类似值传递”(即可以将String看做int,double等基本类型,这些基本类型作为参数是值传递,但String在java规范中又不是基本类型,所以此处称其为“类似值传递”)。在java中是将String看做引用类型的,既然如此,那此处到底是不是引用传递呢?可以明确的告诉大家,这里绝对是引用传递。那既然是引用传递,为什么却时值传递的效果呢?答案其实已经在开篇告诉大家了:java语言规范规定,String型变量指向的内存空间中的内容是不能被改变的。下面来详细解析一下,当我们刚开始调用setString()函数的时候,strTest和str同时指向了"ab”所在的内存单元,然后在setString()中试图改变str的值,根据String的不可改变性,此时系统会分配新的内存并令str指向该内存,该内存的内容为abc;而对于strTest他仍然指向原来的内存单元----即ab所在的内存单元,此处和示例二的道理相同。到这里读者应该明白原因了吧,其实仍然是引用传递,只不过由于String的不可变的性质,导致String变量作为参数的特殊性。
StringBuffer不特殊
StringBuffer 跟其他对象一样,属于引用传递。为什么呢?根据上面的分析可以看出String与StringBuffer时相反的。StringBuffer是可变字符串对象。就是说您可以根据藏宝图改变宝藏(对象)。有另外一种情况容易混淆:有时候传过去的拷贝指针被重新赋值了!也就是说地址值被改变了!复印给佣人的藏宝图被掉包了!它指向的是一堆石头!这种情况下原来主人手里的地图指向的宝藏对象是没有被改变的。可以仔细查看我下面的小例子,右边有每一步打印的结果。
/**
* @author F|sh_cx
*/
public class CX2 {
public static void test(StringBuffer a,StringBuffer b){
a.append(b);
System.out.println("a.hashCode="+a.hashCode());//(2) a.hashCode=3526198
System.out.println("a="+a);//(3) a=AB
System.out.println("b.hashCode="+b.hashCode());//(4) b.hashCode=7699183
b = a;
System.out.println("b.hashCode="+b.hashCode());//(5) b.hashCode=3526198
}
public static void main(String[] args) {
StringBuffer a = new StringBuffer("A");
System.out.println(a.hashCode());//(1)3526198
StringBuffer b = new StringBuffer("B");
test(a,b);
System.out.println(a.hashCode()+","+b.hashCode());//(6) 3526198,7699183
System.out.println("a:"+a);//(7) a:AB
System.out.println("b:"+b);//(8) b:B
}
}