今天在寫代碼的時候遇到了一個參數傳遞的問題,雖然我知道問題出現在哪裡,但是問題的原因似懂非懂,看了很久之前寫的一篇部落格,在網上搜尋了幾篇文章
看了下,終於徹底弄懂了這個問題的原因,寫篇文章總結下。
首先我們得明白幾個概念:按值傳遞,按引用傳遞,棧記憶體,堆記憶體的區別。
一按值傳遞:按值傳遞傳遞的是數值,傳遞的時候會在記憶體中另開闢一塊空間來儲存這個數值,如:
public static void main(String[] args){double a = 3.0;double b = 3.0;Test test = new Test();test.play(a,b);System.out.println("a:"+a);System.out.println("b:"+b);}public void play(double a,double b){a = 0.0;b = 0.0;}
transformLon這個函數中的參數就是按值傳遞的,a,b傳遞進去後會在記憶體中另開闢一塊記憶體空間來儲存a,b的值,所以函數裡面對a,b的更改不會引起函數外面a,b值的改變
a,b列印出來後的結果為: 可以看出a,b的值並未改變。
二按引用傳遞:按引用傳遞其實也是可以理解為按值傳遞,只不過他這個值不是直接的數值,而是儲存數值的地址,但是我們操作修改的卻是數值,而不是地址
故按值傳遞時,會改變前面傳過來的對象。如:
public static void main(String[] args){String array[] = new String[]{"hello world"};Test test = new Test();System.out.println("調用play方法前的array[0]:"+array[0]);test.play(array);System.out.println("調用play方法後的array[0]:"+array[0]);}public void play(String array[]){array[0] = "gaga";}
我們來看一下列印的結果: 可以看出array[0]的值在調用play方法前後是不一樣的,與前面的按值傳遞效
果相反。
三棧記憶體和堆記憶體:
棧記憶體是用來儲存儲存基礎資料型別 (Elementary Data Type)的資料和引用變數及對象。java中的基礎資料型別 (Elementary Data Type)的數值都是儲存在棧中,程式員不能夠直接的設定堆或棧。棧的主要優點為存取
速度快,缺點為存在於棧的資料大小和生存周期是確定的,缺乏靈活性。堆用於存放所有由new關鍵字所建立對象指向的數值的地址,並不是這個對象的地址。如:
由new 所建立的封裝資料類型,Double,String 等 對象的數值則是儲存在堆中。在堆中分配的記憶體由java虛擬機器的記憶體回收行程(Garbage Collection)GC自動回收。
具體棧和堆的區別可以去看一下我之前寫的一篇文章:java中堆和棧的區別。
介紹了完了按值傳遞,按應用傳遞以及棧記憶體和堆記憶體之後,我們從記憶體方面來分析按值傳遞和按引用傳遞的區別。
首先我們看一下按值傳遞時,代碼的記憶體模型圖:
我們看一下按值傳遞時,a的記憶體模型的變遷,可以看出當 定義 doube a = 3.0;時程式會在棧記憶體中分配一塊空間,將數值3.0儲存在這塊空間中,當將a 作為參數傳遞
給play方法時,程式會在記憶體中另外開闢一塊空間A,用來儲存a的數值3.0,當在方法play中對參數a進行了修改,a的值並未改變,改變的是A記憶體中的數值,他由3.0變
成了0.0。A中記憶體的數值和a中記憶體的數值沒有任何關係。
上面就是按值傳遞的原理。
接下來我們來瞭解一下,按引用傳遞時,代碼的記憶體模型:
我們看下按引用傳遞時的記憶體模型,瞭解引用傳遞時的記憶體模型就必須瞭解棧記憶體和堆記憶體,之前已經介紹過了,就不在這裡在贅述了。從這個模型我們可以知道
當new 出Array數組時,會在棧記憶體中分配一地區,array數組有多少個元素就會分配多少塊地區 。假設array[0]對象分配的是“地址a”,array[0]的數值則是在堆記憶體
中開闢了一塊地區用於儲存,當array[0]作為參數傳遞到play方法當中時,程式會在棧記憶體中分配一塊地區儲存array[0]對象(不是array[0]的數值,堆記憶體中只能通過
new 關鍵字來開闢地區)即:地址a,當我們在play方法中修改array[0]對象的數值時,其實是修改堆中儲存的數值,而不是array[0]對象。於是堆中的數值由gaga被
改變為hello world,於是當我們列印出array[0]修改前後的數值時,可以看出已經改變了。這就是按引用傳遞時的原理。
按引用傳遞,其實也是按值傳遞,只不過他傳遞的是數值的地址,而不是數值。我們只要記住,凡是通過new關鍵字建立出來的對象,進行傳遞時都是按引用傳遞
基礎資料型別 (Elementary Data Type)的傳遞都是按值傳遞。按應用傳遞傳遞的是棧中的地址,按值傳遞傳遞的是棧記憶體中的值,這就是按值傳遞和按引用傳遞的主要區別。
參考資料:
http://www.shaccp.com/azxxx/2012/2005.html
http://blog.sina.com.cn/s/blog_59ca2c2a0100qhjx.html