一区二区三区日韩精品-日韩经典一区二区三区-五月激情综合丁香婷婷-欧美精品中文字幕专区

分享

String值傳遞,引用傳遞

 hh3755 2011-06-21
轉(zhuǎn)載文章:http://www./topic/412531

剛才看見一個兄弟在為Java的String傳值/傳引用問題困惑,翻箱倒柜找到了這篇我很久以前寫的文章,發(fā)在這里,希望能對迷惑的朋友有些幫助。
 


提要:本文從實現(xiàn)原理的角度上闡述和剖析了:在Java語言中,以String作為類型的變量在作為方法參數(shù)時所表現(xiàn)出的“非對象”的特性。 

一、 最開始的示例 
寫代碼最重要的就是實踐,不經(jīng)過反復(fù)試驗而得出的說辭只能說是憑空遐想罷了。所以,在本文中首先以一個簡單示例來拋出核心話題: 

public class StringAsParamOfMethodDemo { 

public static void main(String[] args) { 
StringAsParamOfMethodDemo StringAsParamOfMethodDemo = 
new StringAsParamOfMethodDemo(); 
StringAsParamOfMethodDemo.testA(); 
} 

private void testA() { 
String originalStr = "original"; 
System.out.println("Test A Begin:"); 
System.out.println("The outer String: " + originalStr); 
simpleChangeString(originalStr); 
System.out.println("The outer String after inner change: " + originalStr); 
System.out.println("Test A End."); 
System.out.println(); 
} 

public void simpleChangeString(String original) { 
original = original + " is changed!"; 
System.out.println("The changed inner String: " + original); 
} 

} 

這段代碼的邏輯是這樣的:先賦值一個String類型的局部變量,然后把這個變量作為參數(shù)送進一個方法中,在這個方法中改變該變量的值。編譯運行之后,發(fā)現(xiàn)輸出結(jié)果是這樣的: 

Test A Begin: 
The outer String: original 
The changed inner String: original is changed! 
The outer String after inner change: original 
Test A End. 

這個結(jié)果表明在方法內(nèi)部對String類型的變量的重新賦值操作并沒有對這個變量的原型產(chǎn)生任何影響。好了,這個示例的邏輯和運行結(jié)果都展示清楚了,接下來我們來對這個小程序進行分析。在這之前我們先來回顧下Java中所謂的“傳值”和“傳引用”問題。 

二、 Java中的“傳值”和“傳引用”問題 
許多初學(xué)Java的程序員都在這個問題上有所思索,那是因為這是所謂的“C語言的傳值和傳指針問題”在Java語言上同類表現(xiàn)。 
最后得出的結(jié)論是: 
在Java中,當基本類型作為參數(shù)傳入方法時,無論該參數(shù)在方法內(nèi)怎樣被改變,外部的變量原型總是不變的,代碼類似上面的示例: 

int number = 0; 
changeNumber(number) {number++}; //改變送進的int變量 
System.out.println(number); //這時number依然為0 

這就叫做“值傳遞”,即方法操作的是參數(shù)變量(也就是原型變量的一個值的拷貝)改變的也只是原型變量的一個拷貝而已,而非變量本身。所以變量原型并不會隨之改變。 

但當方法傳入的參數(shù)為非基本類型時(也就是說是一個對象類型的變量), 方法改變參數(shù)變量的同時變量原型也會隨之改變,代碼同樣類似上面的示例: 

StringBuffer strBuf = new StringBuffer(“original”); 
changeStringBuffer(strBuf) {strbuf.apend(“ is changed!”)} //改變送進的StringBuffer變量 
System.out.println(strBuf); //這時strBuf的值就變?yōu)榱薿riginal is changed! 

這種特性就叫做“引用傳遞”,也叫做傳址,即方法操作參數(shù)變量時是拷貝了變量的引用,而后通過引用找到變量(在這里是對象)的真正地址,并對其進行操作。當該方法結(jié)束后,方法內(nèi)部的那個參數(shù)變量隨之消失。但是要知道這個變量只是對象的一個引用而已,它只是指向了對象所在的真實地址,而非對象本身,所以它的消失并不會帶來什么負面影響?;仡^來看原型變量,原型變量本質(zhì)上也是那個對象的一個引用(和參數(shù)變量是一樣一樣的),當初對參數(shù)變量所指對象的改變就根本就是對原型變量所指對象的改變。所以原型變量所代表的對象就這樣被改變了,而且這種改變被保存了下來。 

了解了這個經(jīng)典問題,很多細心的讀者肯定會立刻提出新的疑問:“可是String類型在Java語言中屬于非基本類型?。∷诜椒ㄖ械母淖?yōu)槭裁礇]有被保存下來呢!”的確,這是個問題,而且這個新疑問幾乎推翻了那個經(jīng)典問題的全部結(jié)論。真是這樣么?好,現(xiàn)在我們就來繼續(xù)分析。 

三、 關(guān)于String參數(shù)傳遞問題的曲解之一——直接賦值與對象賦值 
String類型的變量作為參數(shù)時怎么會像基本類型變量那樣以傳值方式傳遞呢?關(guān)于這個問題,有些朋友給出過解釋,但可惜并不正確。 
一種解釋就是,對String類型的變量賦值時并沒有new出對象,而是直接用字符串賦值,所以Java就把這個String類型的變量當作基本類型看待了。即,應(yīng)該String str = new String(“original”);,而不是String str = “original”;。這是問題所在么?我們來為先前的示例稍微改造下,運行之后看看結(jié)果就知道了。改造后的代碼如下: 

private void testB() { 
String originalStr = new String("original"); 
System.out.println("Test B Begin:"); 
System.out.println("The outer String: " + originalStr); 
changeNewString(originalStr); 
System.out.println("The outer String after inner change: " + originalStr); 
System.out.println("Test B End:"); 
System.out.println(); 
} 

public void changeNewString(String original) { 
original = new String(original + " is changed!"); 
System.out.println("The changed inner String: " + original); 
} 

我們來看看這次運行結(jié)果是怎么樣的: 

Test B Begin: 
The outer String: original 
The changed inner String: original is changed! 
The outer String after inner change: original 
Test B End. 

實踐證明,這種說法是錯的。 
實際上,字符串直接賦值和用new出的對象賦值的區(qū)別僅僅在于存儲方式不同。 
簡單說明下: 
字符串直接賦值時,String類型的變量所引用的值是存儲在類的常量池中的。因為”original”本身是個字符串常量,另一方面String是個不可變類型,所以這個String類型的變量相當于是滴對一個常量的引用。這種情況下,變量的內(nèi)存空間大小是在編譯期就已經(jīng)確定的。 
而new對象的方式是將”original”存儲到String對象的內(nèi)存空間中,而這個存儲動作是在運行期進行的。在這種情況下,Java并不是把”original”這個字符串當作常量對待的,因為這時它是作為創(chuàng)建String對象的參數(shù)出現(xiàn)的。 
所以對String的賦值方式和其參數(shù)傳值問題并沒有直接聯(lián)系。總之,這種解釋并不是正解。 
四、 關(guān)于String參數(shù)傳遞問題的曲解之二——“=”變值與方法變值 
又有些朋友認為,變值不同步的問題是處在改變值的方式上。 
這種說法認為:“在Java 中,改變參數(shù)的值有兩種情況,第一種,使用賦值號“=”直接進行賦值使其改變;第二種,對于某些對象的引用,通過一定途徑對其成員數(shù)據(jù)進行改變,如通過對象的本身的方法。對于第一種情況,其改變不會影響到被傳入該參數(shù)變量的方法以外的數(shù)據(jù),或者直接說源數(shù)據(jù)。而第二種方法,則相反,會影響到源數(shù)據(jù)——因為引用指示的對象沒有變,對其成員數(shù)據(jù)進行改變則實質(zhì)上是改變的該對象?!?/span> 
這種方式聽起來似乎有些…,我們還是用老辦法,編寫demo,做個小試驗,代碼如下: 

private void testC() { 
String originalStr = new String("original"); 
System.out.println("Test C Begin:"); 
System.out.println("The outer String: " + originalStr); 
changeStrWithMethod(originalStr); 
System.out.println("The outer String after inner change: " + originalStr); 
System.out.println("Test C End."); 
System.out.println(); 
} 

private static void changeStrWithMethod(String original) { 
original = original.concat(" is changed!"); 
System.out.println("The changed inner String: " + original); 
} 

結(jié)果如下: 

Test C Begin: 
The outer String: original 
The changed inner String: original is changed! 
The outer String after inner change: original 
Test C End. 

怎么樣,這證明了問題并不是出在這,又一個解釋在實踐論據(jù)下夭折了。 
那到底是什么原因?qū)е铝诉@種狀況呢? 
好了,不賣關(guān)子了,下面說下我的解釋。 

五、 String參數(shù)傳遞問題的癥結(jié)所在 
其實,要想真正理解一個類或者一個API/框架的最直接的方法就是看源碼。 
下面我們來看看new出String對象的那小段代碼(String類中),也就是String類的構(gòu)造函數(shù): 

    public String(String original) { 
int size = original.count; 
char[] originalValue = original.value; 
char[] v; 
  if (originalValue.length > size) { 
    // The array representing the String is bigger than the new 
    // String itself.  Perhaps this constructor is being called 
    // in order to trim the baggage, so make a copy of the array. 
            int off = original.offset; 
            v = Arrays.copyOfRange(originalValue, off, off+size); 
} else { 
    // The array representing the String is the same 
    // size as the String, so no point in making a copy. 
    v = originalValue; 
} 
this.offset = 0; 
this.count = size; 
this.value = v; 
} 

也許你注意到了里面的char[],這說明對String的存儲實際上通過char[]來實現(xiàn)的。怎么樣?其實就是一層窗戶紙。不知道大家還記不記得在Java API中定義的那些基本類型的包裝類。比如Integer是int包裝類、Float是float的包裝類等等。對這些包裝類的值操作實際上都是通過對其對應(yīng)的基本類型操作而實現(xiàn)的。是不是有所感悟了?對,String就相當于是char[]的包裝類。包裝類的特質(zhì)之一就是在對其值進行操作時會體現(xiàn)出其對應(yīng)的基本類型的性質(zhì)。在參數(shù)傳遞時,包裝類就是如此體現(xiàn)的。所以,對于String在這種情況下的展現(xiàn)結(jié)果的解釋就自然而然得出了。同樣的,Integer、Float等這些包裝類和String在這種情況下的表現(xiàn)是相同的,具體的分析在這里就省略了,有興趣的朋友可以自己做做試驗。 
這也就是為什么若組串操作是通過不同方法來實現(xiàn)的時候,推薦大家使用StringBuffer的真正原因了。至于StringBuffer為什么不會表現(xiàn)出String這種現(xiàn)象,大家再看看的StringBuffer的實現(xiàn)就會明白了,在此也不再贅述了。 
六、 寫在最后 
由此String類型的參數(shù)傳遞問題的原理也就展現(xiàn)出來了。其實可以看出,只要分析方式正確,思考終究得出正確結(jié)論的。 
正確分析方法的基礎(chǔ)有二: 
1、 多實踐:手千萬不要犯懶,實踐必會出真知。 
2、 基于原理:搞清楚程序邏輯的最直接最簡單的方式就是看源碼,這毋庸置疑。 
只要基于這兩個基礎(chǔ)進行分析,在很多情況下會達到事半功倍的效果。這算是經(jīng)驗之談吧,也算是分析程序的“捷徑”方式之一。 

    本站是提供個人知識管理的網(wǎng)絡(luò)存儲空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點。請注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購買等信息,謹防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請點擊一鍵舉報。
    轉(zhuǎn)藏 分享 獻花(0

    0條評論

    發(fā)表

    請遵守用戶 評論公約

    類似文章 更多

    久久99亚洲小姐精品综合| 久久精品一区二区少妇| 国产一区二区三中文字幕 | 精品国产亚洲区久久露脸| 人妻久久这里只有精品| 欧美人妻一区二区三区| 亚洲av秘片一区二区三区| 日韩欧美黄色一级视频| 九九热视频免费在线视频| 国产色第一区不卡高清| 国产日韩精品欧美综合区| 亚洲天堂一区在线播放| 五月综合婷婷在线伊人| 欧美黑人暴力猛交精品| 国产老熟女乱子人伦视频| 日韩一级欧美一级久久| 男人的天堂的视频东京热| 国产在线日韩精品欧美| 麻豆剧果冻传媒一二三区| 亚洲国产av在线观看一区| 欧美国产日产综合精品| 日本一本不卡免费视频| 国产丝袜美女诱惑一区二区| 黑人巨大精品欧美一区二区区| 中文字幕免费观看亚洲视频| 风韵人妻丰满熟妇老熟女av| 亚洲中文字幕人妻系列| 国产成人精品一区二区三区| 欧美日韩国产福利在线观看| 久久福利视频在线观看 | 欧美大胆美女a级视频| 亚洲国产91精品视频| 国产av熟女一区二区三区四区| 亚洲黄色在线观看免费高清| 狠色婷婷久久一区二区三区| 久久热中文字幕在线视频| 免费观看潮喷到高潮大叫| 大伊香蕉一区二区三区| 不卡视频免费一区二区三区| 俄罗斯胖女人性生活视频| 精品一区二区三区不卡少妇av|