亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

簡(jiǎn)單談?wù)凧ava中String類型的參數(shù)傳遞問題

 更新時(shí)間:2015年12月07日 11:58:27   投稿:hebedich  
這篇文章主要介紹了簡(jiǎn)單談?wù)凧ava中String類型的參數(shù)傳遞問題的相關(guān)資料,需要的朋友可以參考下

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

一、最開始的示例

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

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); 
    } 
} 

這段代碼的邏輯是這樣的:先賦值一個(gè)String類型的局部變量,然后把這個(gè)變量作為參數(shù)送進(jìn)一個(gè)方法中,在這個(gè)方法中改變?cè)撟兞康闹?。編譯運(yùn)行之后,發(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.

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

二、Java中的“傳值”和“傳引用”問題

許多初學(xué)Java的程序員都在這個(gè)問題上有所思索,那是因?yàn)檫@是所謂的“C語言的傳值和傳指針問題”在Java語言上同類表現(xiàn)。

最后得出的結(jié)論是:

在Java中,當(dāng)基本類型作為參數(shù)傳入方法時(shí),無論該參數(shù)在方法內(nèi)怎樣被改變,外部的變量原型總是不變的,代碼類似上面的示例:

int number = 0; 

changeNumber(number) {number++}; //改變送進(jìn)的int變量 

System.out.println(number); //這時(shí)number依然為0
這就叫做“值傳遞”,即方法操作的是參數(shù)變量(也就是原型變量的一個(gè)值的拷貝)改變的也只是原型變量的一個(gè)拷貝而已,而非變量本身。所以變量原型并不會(huì)隨之改變。

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

StringBuffer strBuf = new StringBuffer(“original”); 

changeStringBuffer(strBuf) {strbuf.apend(“ is changed!”)} //改變送進(jìn)的StringBuffer變量 

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

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

三、關(guān)于String參數(shù)傳遞問題的曲解之一——直接賦值與對(duì)象賦值

String類型的變量作為參數(shù)時(shí)怎么會(huì)像基本類型變量那樣以傳值方式傳遞呢?關(guān)于這個(gè)問題,有些朋友給出過解釋,但可惜并不正確。
一種解釋就是,對(duì)String類型的變量賦值時(shí)并沒有new出對(duì)象,而是直接用字符串賦值,所以Java就把這個(gè)String類型的變量當(dāng)作基本類型看待了。即,應(yīng)該String str = new String(“original”);,而不是String str = “original”;。這是問題所在么?我們來為先前的示例稍微改造下,運(yùn)行之后看看結(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); 
   } 

我們來看看這次運(yùn)行結(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.

實(shí)踐證明,這種說法是錯(cuò)的。

實(shí)際上,字符串直接賦值和用new出的對(duì)象賦值的區(qū)別僅僅在于存儲(chǔ)方式不同。

簡(jiǎn)單說明下:

字符串直接賦值時(shí),String類型的變量所引用的值是存儲(chǔ)在類的常量池中的。因?yàn)閛riginal本身是個(gè)字符串常量,另一方面String是個(gè)不可變類型,所以這個(gè)String類型的變量相當(dāng)于是對(duì)一個(gè)常量的引用。這種情況下,變量的內(nèi)存空間大小是在編譯期就已經(jīng)確定的。

而new對(duì)象的方式是將original存儲(chǔ)到String對(duì)象的內(nèi)存空間中,而這個(gè)存儲(chǔ)動(dòng)作是在運(yùn)行期進(jìn)行的。在這種情況下,Java并不是把original這個(gè)字符串當(dāng)作常量對(duì)待的,因?yàn)檫@時(shí)它是作為創(chuàng)建String對(duì)象的參數(shù)出現(xiàn)的。

所以對(duì)String的賦值方式和其參數(shù)傳值問題并沒有直接聯(lián)系??傊?,這種解釋并不是正解。

四、關(guān)于String參數(shù)傳遞問題的曲解之二——“=”變值與方法變值

又有些朋友認(rèn)為,變值不同步的問題是處在改變值的方式上。

這種說法認(rèn)為:“在Java 中,改變參數(shù)的值有兩種情況,第一種,使用賦值號(hào)“=”直接進(jìn)行賦值使其改變;第二種,對(duì)于某些對(duì)象的引用,通過一定途徑對(duì)其成員數(shù)據(jù)進(jìn)行改變,如通過對(duì)象的本身的方法。對(duì)于第一種情況,其改變不會(huì)影響到被傳入該參數(shù)變量的方法以外的數(shù)據(jù),或者直接說源數(shù)據(jù)。而第二種方法,則相反,會(huì)影響到源數(shù)據(jù)——因?yàn)橐弥甘镜膶?duì)象沒有變,對(duì)其成員數(shù)據(jù)進(jìn)行改變則實(shí)質(zhì)上是改變的該對(duì)象?!?/p>

這種方式聽起來似乎有些…,我們還是用老辦法,編寫demo,做個(gè)小試驗(yàn),代碼如下:

    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.

怎么樣,這證明了問題并不是出在這,又一個(gè)解釋在實(shí)踐論據(jù)下夭折了。

那到底是什么原因?qū)е铝诉@種狀況呢?

好了,不賣關(guān)子了,下面說下我的解釋。

五、String參數(shù)傳遞問題的癥結(jié)所在

其實(shí),要想真正理解一個(gè)類或者一個(gè)API框架的最直接的方法就是看源碼。

下面我們來看看new出String對(duì)象的那小段代碼(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[],這說明對(duì)String的存儲(chǔ)實(shí)際上通過char[]來實(shí)現(xiàn)的。怎么樣?其實(shí)就是一層窗戶紙。不知道大家還記不記得在Java API中定義的那些基本類型的包裝類。比如Integer是int包裝類、Float是float的包裝類等等。對(duì)這些包裝類的值操作實(shí)際上都是通過對(duì)其對(duì)應(yīng)的基本類型操作而實(shí)現(xiàn)的。是不是有所感悟了?對(duì),String就相當(dāng)于是char[]的包裝類。包裝類的特質(zhì)之一就是在對(duì)其值進(jìn)行操作時(shí)會(huì)體現(xiàn)出其對(duì)應(yīng)的基本類型的性質(zhì)。在參數(shù)傳遞時(shí),包裝類就是如此體現(xiàn)的。所以,對(duì)于String在這種情況下的展現(xiàn)結(jié)果的解釋就自然而然得出了。同樣的,Integer、Float等這些包裝類和String在這種情況下的表現(xiàn)是相同的,具體的分析在這里就省略了,有興趣的朋友可以自己做做試驗(yàn)。

這也就是為什么當(dāng)對(duì)字符串的操作在通過不同方法來實(shí)現(xiàn)的時(shí)候,推薦大家使用StringBuffer的真正原因了。至于StringBuffer為什么不會(huì)表現(xiàn)出String這種現(xiàn)象,大家再看看的StringBuffer的實(shí)現(xiàn)就會(huì)明白了,在此也不再贅述了。

六、寫在最后

由此String類型的參數(shù)傳遞問題的原理也就展現(xiàn)出來了。其實(shí)可以看出,只要分析方式正確,思考終究得出正確結(jié)論的。
正確分析方法的基礎(chǔ)有二:

1、多實(shí)踐:手千萬不要犯懶,實(shí)踐必會(huì)出真知。

2、基于原理:搞清楚程序邏輯的最直接最簡(jiǎn)單的方式就是看源碼,這毋庸置疑。

只要基于這兩個(gè)基礎(chǔ)進(jìn)行分析,在很多情況下會(huì)達(dá)到事半功倍的效果。這算是經(jīng)驗(yàn)之談吧,也算是分析程序的“捷徑”方式之一。

相關(guān)文章

  • 解決springboot 獲取form-data里的file文件的問題

    解決springboot 獲取form-data里的file文件的問題

    這篇文章主要介紹了解決springboot 獲取form-data里的file文件的問題的相關(guān)資料,這里提供了詳細(xì)的解決步驟,需要的朋友可以參考下
    2017-07-07
  • Java Condition條件變量提高線程通信效率

    Java Condition條件變量提高線程通信效率

    這篇文章主要介紹了Java Condition條件變量提高線程通信效率,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2020-10-10
  • 如何利用JConsole觀察分析Java程序的運(yùn)行并進(jìn)行排錯(cuò)調(diào)優(yōu)

    如何利用JConsole觀察分析Java程序的運(yùn)行并進(jìn)行排錯(cuò)調(diào)優(yōu)

    從Java 5開始 引入了 JConsole。JConsole 是一個(gè)內(nèi)置 Java 性能分析器,可以從命令行或在 GUI shell 中運(yùn)行。您可以輕松地使用 JConsole(或者,它更高端的 “近親” VisualVM )來監(jiān)控 Java 應(yīng)用程序性能和跟蹤 Java 中的代碼
    2015-12-12
  • Java關(guān)于JDK1.8中的Optional類

    Java關(guān)于JDK1.8中的Optional類

    本文主要介紹了Optional類的一些常用方法,以及其應(yīng)用場(chǎng)景,其主要是為了規(guī)避空指針異常(NPE)。熟練的運(yùn)用Optional類可以很大的簡(jiǎn)化我們的代碼,使代碼簡(jiǎn)潔明了。,需要的朋友可以參考下面文章內(nèi)容
    2021-09-09
  • java正則表達(dá)式用法大全(深度好文)

    java正則表達(dá)式用法大全(深度好文)

    這篇文章主要給大家介紹了關(guān)于java正則表達(dá)式用法大全的相關(guān)資料,正則表達(dá)式在處理字符串時(shí)非常有用,文中通過實(shí)例代碼介紹的非常詳細(xì),需要的朋友可以參考下
    2023-10-10
  • Spring RestTemplate的使用與踩坑

    Spring RestTemplate的使用與踩坑

    RestTemplate是一個(gè)執(zhí)行HTTP請(qǐng)求的同步阻塞式工具類,它僅僅只是在HTTP客戶端庫基礎(chǔ)上,封裝了更加簡(jiǎn)單易用的模板方法API,方便程序員利用已提供的模板方法發(fā)起網(wǎng)絡(luò)請(qǐng)求和處理,能很大程度上提升我們的開發(fā)效率
    2023-02-02
  • springboot中pom.xml文件注入test測(cè)試依賴時(shí)報(bào)錯(cuò)的解決

    springboot中pom.xml文件注入test測(cè)試依賴時(shí)報(bào)錯(cuò)的解決

    這篇文章主要介紹了springboot中pom.xml文件注入test測(cè)試依賴時(shí)報(bào)錯(cuò)的解決,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2022-03-03
  • restTemplate實(shí)現(xiàn)跨服務(wù)API調(diào)用方式

    restTemplate實(shí)現(xiàn)跨服務(wù)API調(diào)用方式

    這篇文章主要介紹了restTemplate實(shí)現(xiàn)跨服務(wù)API調(diào)用方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。
    2023-07-07
  • RocketMQ消息存儲(chǔ)文件的加載與恢復(fù)機(jī)制源碼分析

    RocketMQ消息存儲(chǔ)文件的加載與恢復(fù)機(jī)制源碼分析

    這篇文章主要介紹了RocketMQ源碼分析之消息存儲(chǔ)文件的加載與恢復(fù)機(jī)制詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2023-05-05
  • SpringMVC中的ResourceUrlProviderExposingInterceptor詳解

    SpringMVC中的ResourceUrlProviderExposingInterceptor詳解

    這篇文章主要介紹了SpringMVC中的ResourceUrlProviderExposingInterceptor詳解,ResourceUrlProviderExposingInterceptor是Spring MVC的一個(gè)HandlerInterceptor,用于向請(qǐng)求添加一個(gè)屬性,需要的朋友可以參考下
    2023-12-12

最新評(píng)論