C#與java值傳遞區別解析

首先要分清幾個概念: “值類型”與“引用類型”;“值傳遞”與“引用傳遞”。以下是這幾個概念的解釋:
1 值類型: 存儲在線程棧裏的數據類型,如int型;
2 引用類型:存儲在託管堆裏的數據類型,如Date型,或自定義class對象;
3 值傳遞: 每次傳遞變量時,都是對棧裏的原始值進行拷貝,如棧地址0001處存放一個整數值4,值傳遞時,先copy整數值4,將之存放在棧地址0002處的內存空間,再將棧地址0002進行傳遞;
4 引用傳遞: 每次傳遞變量時,直接傳遞棧地址,如棧地址0001處存放一個整數值4,引用傳遞時,直接傳遞棧地址0001,而不做複製。

        對於引用類型的變量而言,它們的“內容”存放在託管堆裏,而在線程棧裏存放的,是“內容”的“地址”。如對象在託管堆中的地址是x0001,那麼棧地址0001所指向的內存空間中存放的就是這個對象的內存地址x0001(此處棧地址和託管堆地址並無任何對應關係,由操作系統進行分配)。

        由此大概可以推想,“數據類型”與“傳遞方式”並不是相關的,也就是說“值類型”並不一定對應“值傳遞”,“引用類型”並不一定對應“引用傳遞”。其實可能的組合有4種: 1 “值類型”進行“值傳遞”;2 “值類型”進行“引用傳遞”;3 “引用類型”進行“值傳遞”;4 “引用類型”進行“引用傳遞”。我們分別針對這四種情況進行說明:
       
1 這種情況是經常見到的。首先定義函數function ChangeValue(int value){ value = 10;} 。int a = 1;ChangeValue(a); Print(a).
        2 這種情況C#支持,而Java不支持。函數定義和調用都要稍作調整。具體如下:function ChangeValue(ref int value){ value = 10;} 。int a = 1;ChangeValue(ref a); Print(a).
        大家應該能猜到,1和2打印出來的結果分別是“1”和“10”。因爲值傳遞,傳遞的是原始值的副本,副本值的改變並不會影響原始值;而引用傳遞,傳遞的是原始值的地址,所以改變的就是原始值本身(引用傳遞在C#中以ref或out關鍵字標識)


        對於引用類型而言,也是一樣。例如:
        3 Person p = new Person(“Tony”);定義函數: function ChangeValue(Person p){Person new_p = new Person("John"); p = new_p;},調用 ChangeValue(p); Print(p.Name);

        4   Person p = new Person(“Tony”);定義函數: function ChangeValue(ref Person p){Person new_p = new Person("John"); p = new_p;},調用 ChangeValue(ref p); Print(p.Name);
         對於情況3,打印的結果還是“Tony”,因爲傳遞的是一個副本,那麼即使指向的對象變化了,也是副本的變化,與原值無關;對於情況4,使用了ref關鍵字,傳遞的是原始值的地址,所以指向的修改完全是針對原始值的,所以4的打印結果爲“John”。

        有朋友可能會有疑惑: 因爲Java只支持值傳遞,那麼對於情況3裏的引用類型Person來說,也只能進行值傳遞,那麼修改的永遠只能是副本,這樣的話如何修改原對象的屬性值呢?這豈不是出大亂子了?

         其實即使是值傳遞,也是可以正常修改原對象的屬性值的。例如Person類對象p在託管堆裏的地址是x0001,而該地址存放在棧地址0001對應的內存空間中,即0001->x0001,值傳遞時進行副本拷貝,拷貝值x0001到新的棧地址空間中,即0002->x0001,可見原始值和副本值都是x0001,即對象p在託管堆裏的地址。所以我們定義函數ChangeName(Person p){p.Name = "John";}調用 Person p = new Person(“Tony”); ChangeName(p); Print(p.Name);打印的結果是"John"。因爲函數中p.Name = "John",實際是x0001.Name = "John",即修改的是我們想要修改的對象p。

        因爲Java只支持值傳遞,所以當傳遞一個引用類型給函數時,函數內部只能修改該對象的屬性值,卻不可以更改對象的指向本身,即如果引用指向對象p,函數內部是不能將引用指向換成new_p的;即使換了,換的也只是副本的指向而已。

        至於Java爲何這樣設計,我猜是基於安全性的考慮吧。當然c#中用ref和out標識引用傳遞,也不失爲一種好的設計思路。


發佈了9 篇原創文章 · 獲贊 12 · 訪問量 9萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章