標籤:java 值傳遞 引用傳遞
關於JAVA的值傳遞和引用傳遞,翻看了很多資料和部落格,感覺大多數講的很亂,都是自己明白了之後就不講了的樣子,終於算是比較理解這幾個概念了,下面做一個總結。
1、簡單類型的參數傳遞
Java方法的參數是簡單類型的時候,是按值傳遞的 (pass by value)。下面舉一個經典的swap函數:
無法交換值的方法:
package TestTransferPack;public class TestTransfer {public static void main(String[] args) {// TODO Auto-generated method stubint 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 stubint 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 stubint 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 stubint 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中一樣。
大家通常舉String做為特殊的對象來說明對象是按值或者按引用傳遞,卻往往說的亂七八糟,通常是自己明白之後就亂寫,下面一節再討論String的問題。本人也不甚精通,如果寫的有什麼不對的,歡迎指正。
3、String及相關對象的參數傳遞
通俗來說,String是按照值傳遞的。
package TestTransferPack;public class TestTransfer4 {public static void main(String[] args) {// TODO Auto-generated method stubString 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 stubStringBuffer 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 stubStringBuffer 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 stubStringBuffer 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:11671659211442002549hello world world 1167165921
可以看出,index1在第二次賦值之後就不再是原對象。String也是一樣。
著作權聲明:本文為博主原創文章,未經博主允許不得轉載。
JAVA隨筆篇二(深入分析JAVA簡單類型、String和對象的值傳遞和引用傳遞)