值型別和引用型別,形參和實參,傳值和傳引用
只會在呼叫函式的這個作用域中起作用。
傳值呼叫的情況是這樣的:實參把值傳入堆疊然後發生傳遞過程,形參接受這個值,也可以改變這個值,形參可以在自身的函式中有很多變數,可以進行運算,改變他們的值,但問題的關鍵是,這些變數開闢的記憶體空間都是在堆疊中的,在呼叫結束的一瞬間堆疊全都釋放彈棧了,所有的堆疊的記憶體空間都沒了,存放的資料也就跟著消失了。這個就是傳值不影響實參的根本原因。
在函式呼叫中發生的資料傳送是單向的。 即只能把實參的值傳送給形參,而不能把形參的值反向地傳送給實參。 因此在函式呼叫過程中,形參的值發生改變,而實參中的值不會變化。
傳引用:
真正的以
傳遞以後,行參和實參都是同一個物件,只是他們名字不同而已,對行參的修改將影響實參的值。
它其實和傳值基本一樣的傳送過程,但是關鍵就在於在剛開闢堆疊的時候,它放入的是由主調函式放進來的實參變數的地址,被調函式對形參的任何操作都被處理成間接定址,即通過堆疊中存放的地址訪問主調函式中的實參變數,那麼形參在修改的時候,修改的就是實參地址所對應的值,也就是實參的值,雖然隨著堆疊的消失,這個實參地址和形參都消失了,但修改的內容卻不在堆疊所開闢的記憶體中,它一直存在著,而且這個記憶體就是原來用來存放實參的。
何時傳值、何時傳引用:
如果傳遞的引數是基元型別(int,float等)或結構體如果傳遞的引數前有ref或者out關鍵字,那麼就是傳引用呼叫。
如果傳遞的引數是類(class)並且沒有ref或out關鍵字:
如果呼叫的函式中對引數重新進行了地址分配(new操作),那麼執行結果類似傳值呼叫
如呼叫的函式中沒有對引數重新進行了地址分配,直接就是使用了傳遞的引數,執行結果類似傳引用呼叫
舉個例子:
String是final型別,比如String s="123"; s = "abc",後面的“abc”其實是新建了一個string物件,並重新將其地址給了s。所以函式內的string物件是副本,副本重新指向另外一個地址,對本來的string物件無影響。
而StringBuffer非final型別,StringBuffer s=“123”;s=“ABC”其傳入函式的是他的物件地址的副本,還是指向同一物件。在函式內對其副本操作,等同於對原來StringBuffer物件的操作。