关于JAVA的值传递和引用传递,翻看了很多资料和博客,感觉大多数讲的很乱,都是自己明白了之后就不讲了的样子,终于算是比较理解这几个概念了,下面做一个总结。
1、简单类型的参数传递
Java方法的参数是简单类型的时候,是按值传递的 (pass by value)。下面举一个经典的swap函数:
无法交换值的方法:
package TestTransferPack; public class TestTransfer { public static void main(String[] args) { // TODO Auto-generated method stub int a = 1; int b = 5; swap(a, b); System.out.println(a+" "+ b); } public static void swap(int a,int b){ int temp=0; temp = b; b=a; a=temp; } }
result:1 5以参数形式传递简单类型的变量时,实际上是将参数的值作了一个拷贝传进方法函数的,那么在方法函数里再怎么改变其值,其结果都是只改变了拷贝的值,而不是源值。
交换值的方法:
(1)数组
package TestTransferPack; public class TestTransfer1 { public static void main(String[] args) { // TODO Auto-generated method stub int a = 1; int b = 5; int[] test = new int[2]; test[0]=a; test[1]=b; swap(test,0, 1); a = test[0]; b = test[1]; System.out.println(a+" "+ b); } public static void swap(int[] data,int index1,int index2){ int tmp = data[index1]; data[index1] = data[index2]; data[index2] = tmp; } }
result:5 1
这种利用数组的方式可以改变两个简单类型的值。
C++中有指针,简单类型的传递方式和1中的一样,都是值传递,但是却可以通过传递指针的方式交换两个简单类型的值,但是JAVA中没指针,所以简单类型的值交换要特殊处理。
2、普通对象的参数传递
普通对象一般是按照引用传递的,姑且先这样说,也就是大家说的按地址传递, 地址传递传递的就是原来的对象地址,这样修改内容就会改变原值,后面会对这种传递方式做详细分析解释。
package TestTransferPack; public class TestTransfer2 { public static void main(String[] args) { // TODO Auto-generated method stub int a = 1; int b = 5; Test tt1 = new Test(a); Test tt2 = new Test(b); swap1(tt1,tt2); System.out.println(tt1.a+" "+ tt2.a); } public static void swap1(Test index1,Test index2){ int tmp = index1.a; index1.a = index2.a; index2.a = tmp; } public static class Test{ int a=0; public Test(int b){ a = b; } } }
result: 5 1
可以看出,在函数中两个对象的数据是可以修改的。
然而这样却不能改变对象的值:
package TestTransferPack; public class TestTransfer3 { public static void main(String[] args) { // TODO Auto-generated method stub int a = 1; int b = 5; Test tt1 = new Test(a); Test tt2 = new Test(b); swap1(tt1,tt2); System.out.println(tt1.a+" "+ tt2.a); } public static void swap1(Test index1,Test index2){ /*int tmp = index1.a; index1.a = index2.a; index2.a = tmp;*/ Test tmp = new Test(); tmp = index1; index1 = index2; index2 = tmp; } public static class Test{ int a=0; public Test(int b){ a = b; } public Test(){} } }
result: 1 5究其原因,我们可以将去推及C++的参数传递,C++中如果参数传递的是一个指针,那么这个指针指向的对象内容可以改变,但是指针本身确是一个复制,因此不能改变指针的值;JAVA中对象的参数传递是一个地址,这个地址指向的内容是可以改变的,就像上面TestTransfer2中的例子,但是这个地址却是按值传递的,改变这个地址完全没有用,就像TestTransfer3中一样。
3、String及相关对象的参数传递
通俗来说,String是按照值传递的。
package TestTransferPack; public class TestTransfer4 { public static void main(String[] args) { // TODO Auto-generated method stub String tt1 = new String("hello "); String tt2 = new String("world "); swap1(tt1,tt2); System.out.println(tt1 + tt2); } public static void swap1(String index1,String index2){ String tmp = index1; index1 = index2; index2 = tmp; } } result:hello world
这和TestTransfer3是一样的,当然不能改变String的值,有人说用StringBuffer,当然也不能。
package TestTransferPack; public class TestTransfer5 { public static void main(String[] args) { // TODO Auto-generated method stub StringBuffer tt1 = new StringBuffer ("hello "); StringBuffer tt2 = new StringBuffer ("world "); swap1(tt1,tt2); System.out.println(tt1.toString()+ tt2.toString()); } public static void swap1(StringBuffer index1,StringBuffer index2){ StringBuffer tmp = index1; index1 = index2; index2 = tmp; } }
<pre name="code" class="java">result:hello world可能会想到在函数中改变index1的值,比如index1=index2,但是String和StringBuffer都不行,改变的值并不能带出函数。
但是这样却可以:
package TestTransferPack; public class TestTransfer6 { public static void main(String[] args) { // TODO Auto-generated method stub StringBuffer tt1 = new StringBuffer ("hello "); StringBuffer tt2 = new StringBuffer ("world "); append(tt1,tt2); System.out.println(tt1.toString()+ tt2.toString()); } public static void append(StringBuffer index1,StringBuffer index2){ index1.append(index2); } }
result:hello world world下面看一下append的实现,一路追踪下去,我们会找到这句话:
System.arraycopy(value, srcBegin, dst, dstBegin, srcEnd - srcBegin);使用这个方法之前先扩展了StringBuffer的数组大小,然后copy数据。这样,就会改变index1指向的空间的内容。
都说String在赋值操作的过程中产生新的对象,这样的话,TestTransfer4和TestTransfer5中的问题就迎刃而解了,因为都不是一个对象,怎么可能改变这个值。
下面探讨一下。
我们知道,hashCode是object的唯一标识。我们可以用 hashCode来确定String在赋值或者其他操作中的对象变化。
package TestTransferPack; public class TestTransfer7 { public static void main(String[] args) { // TODO Auto-generated method stub StringBuffer tt1 = new StringBuffer ("hello "); StringBuffer tt2 = new StringBuffer ("world "); append(tt1,tt2); System.out.println(tt1.toString()+ tt2.toString()); System.out.println(tt1.hashCode()); } public static void append(StringBuffer index1,StringBuffer index2){ index1.append(index2); System.out.println(index1.hashCode()); index1 = index2; System.out.println(index1.hashCode()); } }
result:1167165921 1442002549 hello world world 1167165921可以看出,index1在第二次赋值之后就不再是原对象。String也是一样。
版权声明:本文为博主原创文章,未经博主允许不得转载。
JAVA随笔篇二(深入分析JAVA简单类型、String和对象的值传递和引用传递)
原文:http://blog.csdn.net/feiyangtianyao/article/details/47662979