一.為什么說Java中只有值傳遞?
對於java中的參數傳遞方式中是否有引用傳遞這個話題,很多的人都認為Java中有引用傳遞,但是我個人的看法是,Java中只有值傳遞,沒有引用傳遞。
那么關於對象的傳遞怎么解釋呢?難道對象不是一個引用傳遞嗎?
對於這一點我的看法是,對象的傳遞只不過是將對象的地址值傳遞到方法里,只要你不改變這個地址值,那他就指向原來的引用不會改變,但是你一旦改變了這個地址值,那么你就改變了他的實際引用。那歸根到底還不是值傳遞嗎?只不過我普通類型傳遞的是copy后的普通類型的值,我引用傳遞的是一個copy的地址值而已。
當然我在舉例子的時候還是將他們分開:
實際的例子如下:
1.值傳遞
package com.cjm.functionparameter; /** * @author 程嘉明 * @version 創建時間:2018/8/9 10:18 * 函數得值傳遞 */ public class Valuetransmit { public static void main(String[] args) { int num=0;//定義一個值 System.out.println("在main中定義的值為:"+num); fan(num); System.out.println("在調用函數的值為:"+num); } public static void fan(int num){ System.out.println("###########進入函數###########"); System.out.println("實參為:"+num+"此時的值沒有被改變!"); //修改函數的參數: ++num; System.out.println("在函數中實參自增后的值為:"+num); System.out.println("###########退出函數###########"); } }
可以看出值傳遞是不會改變他本身的值。
所以Java中你就無法使用一個函數將兩個變量的值交換。
嘗試如下:
package com.cjm.functionparameter; /** * @author 程嘉明 * @version 創建時間:2018/8/9 10:35 */ public class ChangeValue { public static void main(String[] args) { //定義兩個變量 int num1=0; int num2=1; System.out.println("未經過函數的交換的值為:"+"num1="+num1+"\tnum2="+num2); change(num1,num2); System.out.println("經過函數的交換后的值為:"+"num1="+num1+"\tnum2="+num2); } //設置一個”交換函數“ public static void change(int num1,int num2){ int temp=num1; num1=num2; num2=temp; } }
失敗了,Java不能替我們完成這一項任務,但這無傷大雅,因為我們並不想交換兩個變量的值就去定義一個函數,這實在是小題大做了。
2.杠精們的"引用傳遞"
那么首先我需要一個對象,來檢測一下在函數中的對象是否和外面的對象相同。
package com.cjm.functionparameter; /** * @author 程嘉明 * @version 創建時間:2018/8/9 10:53 * 對象的判斷函數實參對象,和傳遞對象是否相同 */ public class ObjectJudge { public static void main(String[] args) { A a=new A(0); System.out.println("main函數中的對象的哈希值為:"+a.hashCode()); System.out.println(); fan(a); } public static void fan(A a){ System.out.println("函數中的對象的哈希值為:"+a.hashCode()); } } class A{ int num; public A(int num) { this.num = num; } }
可以看出這兩個引用指向的對象的哈希值是一樣的,那么也就是說這兩個引用指向的是同一個對象。
那么也是說我們在函數對對象a的操作也會影響外面的對象。
來看下面的操作:
package com.cjm.functionparameter; /** * @author 程嘉明 * @version 創建時間:2018/8/9 10:53 * 對象的判斷函數實參對象,和傳遞對象是否相同 */ public class ObjectJudge { public static void main(String[] args) { A a=new A(0); System.out.println("main函數中的對象的哈希值為:"+a.hashCode()); System.out.println("在main函數中的對象的num為:"+a.num); System.out.println("##########函數開始##########"); fan(a); System.out.println("##########函數之后##########"); System.out.println("在main函數中的對象的num為:"+a.num); } public static void fan(A a){ System.out.println("函數中的對象的哈希值為:"+a.hashCode()); a.num=10; System.out.println("在函數中的對象的num為:"+a.num); } } class A{ int num; public A(int num) { this.num = num; } }
那么我們現在再來修改一下,將函數中的對象修改為其他的引用會發生什么
package com.cjm.functionparameter; /** * @author 程嘉明 * @version 創建時間:2018/8/9 10:53 * 對象的判斷函數實參對象,和傳遞對象是否相同 */ public class ObjectJudge { public static void main(String[] args) { A a=new A(0); System.out.println("main函數中的對象的哈希值為:"+a.hashCode()); System.out.println("在main函數中的對象的num為:"+a.num); System.out.println("##########函數開始##########"); fan(a); System.out.println("##########函數之后##########"); System.out.println("在main函數中的對象的num為:"+a.num); } public static void fan(A a){ System.out.println("為做改變前,函數中的對象的哈希值為:"+a.hashCode()); a=new A(0); System.out.println("做了重新賦值后,函數中的對象的哈希值為:"+a.hashCode()); a.num=10; System.out.println("在函數中的對象的num為:"+a.num); } } class A{ int num; public A(int num) { this.num = num; } }
現在就是我們將 實參重新賦值了一個新的對象,那么我們可以發現a指向的對象已經改變,所以當我們對a進行操作時,main函數中的實參就不會改變了。
到這里我們可以總結出:Java中的參數傳遞就是值傳遞,只不過普通類型傳遞的是copy后的值,而對象傳遞的就是地址值,只要你不去改變地址值,那么你在函數中對對象的操作也會影響原來的對象。
那么現在又來了一個問題:
3.Java能不能交換兩個對象呢?
直接上代碼:
package com.cjm.functionparameter; /** * @author 程嘉明 * @version 創建時間:2018/8/9 12:26 */ public class ChangeObject { public static void main(String[] args) { A a1=new A(10); A a2=new A(20); System.out.println("在執行函數前:a1的值為:"+a1.num+"a2的值為:"+a2.num); change(a1,a2); System.out.println("在執行函數后:a1的值為:"+a1.num+"a2的值為:"+a2.num); } public static void change(A a1,A a2){ A temp=a1; a1=a2; a2=temp; } }
然而並沒有交換。。。。。。
我們輸出所有的哈希碼:
package com.cjm.functionparameter; /** * @author 程嘉明 * @version 創建時間:2018/8/9 12:26 */ public class ChangeObject { public static void main(String[] args) { A a1=new A(10); A a2=new A(20); System.out.println("在執行函數前:a1的值為:"+a1.num+"a2的值為:"+a2.num); System.out.println("main函數中的a1的哈希碼為:"+a1.hashCode()+"a2的哈希碼為:"+a2.hashCode()); System.out.println("############執行函數#############"); System.out.println(); change(a1,a2); System.out.println("在執行函數后:a1的值為:"+a1.num+"a2的值為:"+a2.num); System.out.println("############函數之后#############"); } public static void change(A a1,A a2){ System.out.println("函數剛開始時的a1的哈希碼為:"+a1.hashCode()+"a2的哈希碼為:"+a2.hashCode()); A temp=a1; a1=a2; a2=temp; System.out.println("函數中的a1的哈希碼為:"+a1.hashCode()+"a2的哈希碼為:"+a2.hashCode()); } }
似乎沒有問題:因為在函數中a1和a2的地址值是改變了,但是這是一個地址值,你修改了他有什么作用呢?
圖解如下:
開始時:
在函數中進行交換:
那么此時main函數中的對象會交換嗎?當然不會!所以還是那一句話:
到這里我們可以總結出:Java中的參數傳遞就是值傳遞,只不過普通類型傳遞的是copy后的值,而對象傳遞的就是地址值,只要你不去改變地址值,那么你在函數中對對象的操作也會影響原來的對象。
4.針對一個特殊類String類
為什么說這個String特殊呢?
因為:面試題里面都是拿他做例子所以他特殊(#手動滑稽)
當然不是了,主要是因為String是final類不能在對象上去修改,只能new一個新的對象。
所以看看下面的代碼你就知道我的意思了:
當你的參數拼接后其實已經改變了你這個對象的引用。而你的形參的類型是final的這也就代表着你不能改變他的引用對像。
講這個似乎對我們這個沒有幫助,那么我們來看看下面的代碼:
package com.cjm.functionparameter; /** * @author 程嘉明 * @version 創建時間:2018/8/9 14:08 * String類型是否能夠在拼接符的作用下不改變對象 */ public class StringAdd { public static void main(String[] args) { String str="101010"; System.out.println("在沒有進入函數之前String的對象的哈希碼為:"+str.hashCode()); stringChange(str); System.out.println("str經過函數的值為:"); System.out.println(str); } public static void stringChange(String str){ System.out.println("進入函數后沒有修改String之前的Str的哈希碼為:"+str.hashCode()); str+="aaaa"; System.out.println("進入函數后修改String之后的Str的哈希碼為:"+str.hashCode()); } }
在修改時我們的值沒有發生改變。
就是拼接后你的String對象已經改變,接下來你所做的就與main函數里的str變量沒有關系了。