您好,登錄后才能下訂單哦!
本篇文章給大家分享的是有關Java 中substring()的工作原理是什么,小編覺得挺實用的,因此分享給大家學習,希望大家閱讀完這篇文章后可以有所收獲,話不多說,跟著小編一起來看看吧。
1、substring() 是干嘛的
sub是 subtract 的縮寫,因此 substring 的字面意思就是“把字符串做個減法”。這樣一分析,是不是感覺方法的命名還是蠻有講究的?
substring() 的完整寫法是 substring(int beginIndex, int endIndex)。該方法返回一個新的字符串,介于原有字符串的起始下標 beginIndex 和結尾下標 endIndex-1 之間。
String cmower = "沉默王二,一枚有趣的程序員"; cmower = cmower.substring(0, 4); System.out.println(cmower);
程序輸出的結果為:
沉默王二
為什么呢?我來簡單解釋一下。
Java 的下標都是從 0 開始編號的(我不確定有沒有從 1 開始的編程語言),這和我們平常生活中從 1 開始編號的習慣不同。Java 這樣做的原因如下:
Java 是基于 C 語言實現的,而 C 語言的下標是從 0 開始的——這聽起來好像是一句廢話。真正的原因是下標并不是下標,在指針(C)語言中,它實際上是一個偏移量,距離開始位置的一個偏移量。第一個元素在開頭,因此它的偏移量就為 0。
此外,還有另外一種說法。早期的計算機資源比較匱乏,0 作為起始下標相比較于 1 作為起始下標,編譯的效率更高。
知道了這層原因后,再來看上面這段代碼,就會豁然開朗。對于“沉默王二,一枚有趣的程序員”這串字符來說,“沉”的下標為 0,“默”的下標為 1,“王”的下標為 2,“二”的下標為 3,所以 cmower.substring(0, 4) 返回的字符串是“沉默王二”——包括起始下標但不包括結尾下標。
2、substring() 在被調用的時候究竟發生了什么?
在此之前,我們已經了解到:字符串是不可變的,因此當調用 substring() 方法的時候,返回的其實是一個新的字符串。那么變量 cmower 的地址引用就會發生如下圖所示的變化。
為了證明上圖是完全正確的,我們來看一下 JDK 7 中 substring() 的源碼。
public String(char value[], int offset, int count) { //check boundary this.value = Arrays.copyOfRange(value, offset, offset + count); } public String substring(int beginIndex, int endIndex) { //check boundary int subLen = endIndex - beginIndex; return new String(value, beginIndex, subLen); }
可以看得出,substring() 通過 new String() 返回了一個新的字符串對象,在創建新的對象時通過 Arrays.copyOfRange() 復制了一個新的字符數組。
但 JDK 6 就有所不同。說到 JDK 6,可能有些讀者表示不服,JDK 6?什么年代了,JDK 13 都出來了好不好?但我想告訴大家的是,對比著剖析 JDK 的源碼,對學習大有裨益。
不是有那么一句話嘛,要想了解一個成功人士,不能只關注他發跡以后的事,更要關注他之前做了什么。
就請隨我來,看看 JDK 6 中的 substring() 的源碼吧。
//JDK 6 String(int offset, int count, char value[]) { this.value = value; this.offset = offset; this.count = count; } public String substring(int beginIndex, int endIndex) { //check boundary return new String(offset + beginIndex, endIndex - beginIndex, value); }
substring() 方法本身和 JDK 7 并沒有很大的差別,都通過 new String() 返回了一個新的字符串對象。但是 String() 這個構造函數有很大的差別,JDK 6 只是簡單地更改了一下兩個屬性(offset 和 count)的值,value 并沒有變。
PS:value 是真正存儲字符的數組,offset 是數組中第一個元素的下標,count 是數組中字符的個數。
這意味著什么呢?
調用 substring() 的時候雖然創建了新的字符串,但字符串的值仍然指向的是內存中的同一個數組,如下圖所示。
3、為什么 JDK 7 的構造函數發生了變化
看了 JDK 6 和 JDK 7 源碼之后,大家可能產生這樣一個疑惑:為什么 JDK 7 要做出改變呢?大家共用同一個字符串數組不是挺好的嘛,省得占用新的內存空間。事實上呢?
如果有一個很長很長的字符串,可以繞地球一周,當我們需要調用 substring() 截取其中很小一段字符串時,就有可能導致性能問題。由于這一小段字符串引用了整個很長很長的字符數組,就導致很長很長的這個字符數組無法被回收,內存一直被占用著,就有可能引發內存泄露。
PS:內存泄露是指由于疏忽或錯誤造成程序未能釋放已經不再使用的內存。
那 JDK 7 出現之前,這個隱患怎么應對呢?答案如下。
cmower = cmower.substring(0, 4) + "";
為什么,為什么,為什么,多一個 “+ ""” 就能解決內存泄漏的問題?有些讀者可能不太相信,我來帶大家分析一下。
首先呢,我們通過 JAD 對字節碼反編譯一下,上面這行代碼就變成了如下內容。
cmower = (new StringBuilder(String.valueOf(cmower.substring(0, 4)))).toString();
“+”號操作符就相當于一個語法糖,加上空的字符串后,會被 JDK 轉化為 StringBuilder 對象,該對象在處理字符串的時候會生成新的字符數組,所以 cmower = cmower.substring(0, 4) + ""; 這行代碼執行后,cmower 就指向了和 substring() 調用之前不同的字符數組。
以上就是Java 中substring()的工作原理是什么,小編相信有部分知識點可能是我們日常工作會見到或用到的。希望你能通過這篇文章學到更多知識。更多詳情敬請關注億速云行業資訊頻道。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。