官术网_书友最值得收藏!

1.14 值傳遞與引用傳遞的區(qū)別

方法調(diào)用是編程語言中非常重要的一個特性,在方法調(diào)用的時候,通常需要傳遞一些參數(shù)來完成特定的功能。Java語言提供了兩種參數(shù)傳遞的方式:值傳遞和引用傳遞。

值傳遞:在方法調(diào)用中,實參會把它的值傳遞給形參,形參只是用實參的值初始化一個臨時的存儲單元,因此形參與實參雖然有著相同的值,但是卻有著不同的存儲單元,因此對形參的改變不會影響實參的值。

引用傳遞:在方法調(diào)用中,傳遞的是對象(也可以看作是對象的地址),這時候形參與實參的對象指向的是同一塊存儲單元,因此對形參的修改就會影響實參的值。

在Java語言中,原始數(shù)據(jù)類型在傳遞參數(shù)的時候都是按值傳遞的,而包裝類型是按引用傳遞的。

下面通過一個例子來介紹按值傳遞和按引用傳遞的區(qū)別。

程序運行結(jié)果為:

按引用傳遞其實跟傳遞指針類似,是把對象的地址作為參數(shù)的,如圖1-4所示。

圖1-4 值傳遞與引用傳遞的區(qū)別

為了便于理解,假設(shè)1和“Hello”存儲的地址分別為0X12345678和0XFFFFFF12。在調(diào)用方法testPassParameter的時候,由于i為基本類型,因此參數(shù)是按值傳遞的,此時會創(chuàng)建一個i的副本,該副本與i有相同的值,把這個副本作為參數(shù)賦值給n,作為傳遞的參數(shù)。而StringBuffer由于是一個類,因此按引用傳遞,傳遞的是它的引用(傳遞的是存儲“Hello”的地址)。

圖1-4中,在testPassParameter內(nèi)部修改的是n的值,這個值與i是沒關(guān)系的。但是在修改ss1的時候,修改的是ss1這個地址指向的字符串,由于形參ss1與實參s1指向的是同一塊存儲空間,因此修改ss1后,s1指向的字符串也被修改了。

下面再從另外一個角度出發(fā)來對引用傳遞進行詳細分析:

對于變量s1而言,它是一個字符串對象的引用,引用的字符串的值是“Hello”,而變量s1的值為0X12345678(可以理解為是“Hello”的地址,或者“Hello”的引用),那么在方法調(diào)用時,參數(shù)傳遞的其實就是s1值的一個副本(0X12345678),如圖1-4所示,ss1的值也為0X12345678。如果在方法調(diào)用的過程中通過ss1(字符串的引用或地址)來修改字符串的內(nèi)容,因為s1與ss1指向同一個字符串,所以,通過ss1對字符串的修改對s1也是可見的。但是方法中對ss1值的修改對s1是沒有影響的,如下例所示:

程序的運行結(jié)果為:

對運行結(jié)果分析可知,在testPassParameter方法中,依然假設(shè)“Hello”的地址為0XFFFFFF12(實際上是s1的值),在方法調(diào)用的時候,首先把s1的副本傳遞給ss1,此時ss1的值也為0XFFFFFF12,通過調(diào)用ss1=new StringBuffer("World")語句實際上是改變了ss1的值(ss1指向了另外一個字符串“World”),但是對形參ss1值的改變對實參s1沒有影響,雖然ss1被改變“World”的引用(或者“World”的地址),s1還是代表字符串“Hello”的引用(或可以理解為s1的值仍然是“Hello”的地址)。從這個角度出發(fā)來看,StringBuffer從本質(zhì)上來講還是值傳遞,它是通過值傳遞的方式來傳遞引用的。

Java中處理八種基本的數(shù)據(jù)類型用的是值傳遞,其他所有類型都用的是引用傳遞,由于這八種基本數(shù)據(jù)類型的包裝類型都是不可變量,因此增加了對“按引用傳遞”的理解難度。下面給出一個示例來說明:

程序的輸出結(jié)果為:

對于上述程序的前兩個輸出“1”和“2”,不少讀者都認為Integer是按值傳遞的而不是按引用傳遞的,其實這是一個理解上的誤區(qū),上述代碼傳遞的還是引用(引用是按值傳遞的),只是由于Integer是不可變類,因此沒有提供改變它的值的方法,在上例中,在執(zhí)行b++后,由于Integer是不可變類,因此此時會創(chuàng)建一個新值為2的Integer賦值給b,此時b與a其實已經(jīng)沒有任何關(guān)系了。

下面通過程序后面的兩個輸出來加深對“按引用傳遞”的理解。為了理解后面兩個輸出結(jié)果,首先必須理解引用也是按值傳遞的。為了便于理解,假設(shè)s1和s2指向字符串的地址分別為0X12345678和0XFFFFFF12,那么在調(diào)用方法changeStringBuffer的時候,傳遞s1與s2的引用就可以理解為傳遞了兩個地址0X12345678和0XFFFFFF12,而且這兩個地址是按值傳遞的(即傳遞了兩個值,ss1為0X12345678,ss2為0XFFFFFF12),在調(diào)用方法ss1.append("World")的時候,會修改ss1所指向的字符串的值,因此會修改調(diào)用者s1的值,得到的輸出結(jié)果為“Hello World”。但是在執(zhí)行ss2=ss1的時候,只會修改ss2的值而對s2毫無影響,因此s2的值在調(diào)用前后保持不變。為了便于理解,圖1-5給出了函數(shù)調(diào)用的處理過程。

圖1-5 不變量的引用傳遞

從圖1-5中可以看出,在傳遞參數(shù)的時候相當于傳遞了兩個地址,然后調(diào)用ss1.append("World")修改了這個地址所指向的字符串的值,而在調(diào)用ss2=ss1時,相當于修改了函數(shù)changeStringBuffer內(nèi)部的局部變量ss2,這個修改與ss1沒關(guān)系。

主站蜘蛛池模板: 寿宁县| 恩施市| 平安县| 洛隆县| 松溪县| 赫章县| 阳泉市| 太仆寺旗| 广东省| 元氏县| 泰州市| 厦门市| 罗源县| 长春市| 义马市| 茂名市| 鄢陵县| 桦甸市| 昌宁县| 武陟县| 大洼县| 商都县| 高邮市| 石嘴山市| 泸州市| 肥乡县| 福鼎市| 呈贡县| 磐安县| 仁布县| 卢湾区| 甘谷县| 南昌县| 桑植县| 工布江达县| 遂平县| 兴山县| 武夷山市| 宜宾市| 靖边县| 定西市|