之前學習C#的時候,是徹底在工做崗位上學習,一些底層較爲深刻的道理都不是很清楚。現在學習了Java,對於Java參數傳遞只能傳值,不能傳引用(指針)感到很困惑,在C#中不是經常說把某個引用傳遞到函數中嗎?甚至C#有至關簡便的ref、out參數,明明白白的傳引用。java
通過一番探索,得出的結論代表,Java中我無論你究竟是傳值仍是傳引用,只須要記住原生數據類型(值類型)和String做爲參數傳遞的時候,其本來的值都不會發生改變;而引用類型在做爲參數傳遞時,函數中對其的操做,都會反饋到引用所指向的值。程序員
這個結論不是和C#如出一轍嘛,除了C#能夠方便的使用ref、out之外……編程
下面對Java中參數傳遞只能傳值進行解釋,事實證實,之前我對於C#的傳值仍是傳引用理解的也不夠深刻:函數
先申明:java中的參數傳遞都是值傳遞,沒有引用傳遞。
值傳遞的概念:值傳遞會爲所傳遞的對象(這裏的對象不是java裏的對象object,而是通常意義上的對象)從新開闢一塊空間,因而對所傳對象的操做不會影響到原對象。
現象:有種說法是java中基本數據類型做參數是值傳遞,對象作參數是引用傳遞。其實是錯誤的,真正的引用傳遞,是相似C語言中的指針傳遞,和C#中的ref參數。
理解:爲何對象作參數也是值傳遞呢?
分析:對象作參數傳遞的是對象的引用,暫且能夠理解成對象的地址。因爲是值傳遞,那麼被傳遞的對象自己是不會有任何變化的,因此該對象的引用是不會發生任何變化的。學習
一、先舉一個原生數據類型的例子:測試
測試函數:this
public static void ValueTrans(int a){ a = 2;//新產生一個值,開闢一個值類型出來,故值不會被修改。 }
測試此函數的代碼:spa
int a = 1; System.out.println("a's value(before) = " + a); ValueTrans(a); System.out.println("a's value(after) = " + a);
打印出的結果:設計
a's value(before) = 1 a's value(after) = 1
安裝Java值傳遞的思想來考慮,這個結果是顯而易見的,pass by value,我只是把這個數值傳給你使用。指針
二、那麼Integer如何理解呢?
測試函數:
public static void ValueTrans2(Integer a){ a = new Integer(2); }
其中a也是一個對象,那麼這麼傳入,是否是傳引用呢?調用了這個函數,a的int值會不會被修改呢?
測試此函數的代碼:
Integer a2 = new Integer(1); System.out.println("a2's value(before) = " + a2); ValueTrans2(a2); System.out.println("a2's value(after) = " + a2);
打印出的結果:
a2's value(before) = 1 a2's value(after) = 1
結果代表,值仍舊沒有被修改。
如何解釋這個現象:
理解這裏的關鍵是區分對象和引用。這裏聲明的a2是一個引用,而不是一個對象(只是Java把它設計爲看上去好像是對象同樣)。這個引用它指向了一個對象,這個對象就是後面用new關鍵字生成的對象。所以,能夠說a2指向了一個Integer對象。
在調用ValueTrans2方法的時候,程序將a2做爲參數傳遞給ValueTrans2方法了。這裏仍然是值傳遞,在ValueTrans2調用過程當中,會產生一份新的引用(不妨叫作y)。此時,a2和y指向了同一個對象。
可是此時,咱們讓y指向另一個對象, y=new Integer(2); 此時a2和y就指向了不一樣的對象。y修改了它指向的對象的屬性,很顯然不會影響到a2指向的對象。
(這裏不妨說明一下引用類型的傳遞,如我把一個引用類型x傳入函數中,這裏也是值傳遞,在函數被調用的過程當中,產生了一個新的引用y,x和y指向同一個對象。此時修改y指向的對象,也即修改x所指向的對象!那麼引用類型的傳遞,會形成x所指向的值被修改)
圖解以下:
三、如何理解String?
若是你理解了2當中Integer的傳遞方式,那麼我想String也不難理解。咱們新建一個String,
String string = "old String";
實際上就是
String string2 = new String("old String");
Java的機制,形成當你使用一個String如"old String"時,會新建一個對象出來。
本着負責任的態度,仍舊把相應的測試函數貼上來:
public static void StringTrans(String string){ string = "new String"; }
測試函數的代碼:
String string = "old String"; System.out.println("string's value(before) = " + string); StringTrans(string); System.out.println("string's value(after) = " + string);
打印出的結果:
string's value(before) = old String string's value(after) = old String
緣由就是,實際上函數StringTrans是複製了一個引用指向原來的對象,可是函數又讓這個引用指向了新的String對象。這不會對原來的引用和原來的對象形成任何影響!
四、如何理解引用類型是傳值?
測試自定義類:
public class People { public int age; public String name; public People(int age, String name){ this.age = age; this.name = name; } }
測試的函數:
public static void ReferTrans(People people){ people.age = 2; people.name = "lisi"; }
測試函數的代碼:
People people = new People(1, "zhangsan"); System.out.println("people's age(before) = " + people.age); System.out.println("people's name(before) = " + people.name); ReferTrans(people); System.out.println("people's age(after) = " + people.age); System.out.println("people's name(after) = " + people.name);
打印的結果:
people's age(before) = 1 people's name(before) = zhangsan people's age(after) = 2 people's name(after) = lisi
設新建的對象是PEOPLE,其引用是people,在調用ReferTrans方法是,仍然是值傳遞,方法賦值了一個引用people2,people2仍舊指向PEOPLE,因此操做people2對對象進行修改,即爲對PEOPLE進行修改(String的話在此時已經new了一個新對象出來),和使用people的效果同樣。表現出現來現象就是,當引用類型傳入函數時,函數中對其的修改,都會表如今原來的引用類型上。
圖解以下:
我一直認爲只給分析過程,不給結論的都是耍流氓的行爲。
結論:
一、在實際的使用過程當中,咱們沒必要糾結那麼多,原生數據類型和String,在傳入函數的時候,無論函數裏面作什麼,他的值都不會被修改。引用類型傳遞到函數中,只要函數中不是讓參數指向一個新建的對象,那麼函數中的修改會反映到此引用類型上。
二、平時的處理上,能夠把String和原生數據類型放在同一類的狀況下使用,由於Java內部使用了不少的機制,讓String看起來跟原生數據類型同樣。可是String歸根結底是一個引用類型,如在進行比較時,其餘原生數據類型可使用==,而String必須使用equals方法才能比較其內部的值(這點C#作的很是好,直接使用==,不讓程序員過度的瞭解底層,我在作C#編程的過程當中,甚至歷來都沒有以爲string和值類型有什麼不一樣)。
三、Java確實是只能傳值,不能傳引用。所謂傳引用,只在C、C++的傳指針和C#的ref中存在。