stringbuilder寻找字符串位置可能存在多个 java_Java 语言基础amp;String
Java 語言基礎
String ?
字符串的不可變性 ?
定義一個字符串
使用變量來賦值變量 ? ?
String s2 = s; ?
s2 保存了相同的引用值, 因為他們代表同一個對象 ?
字符串連接 ?
s = s.concat("ef"); ?
s 中保存的是一個重新創建出來的 string 對象的引用 ?
總結
一旦一個 string 對象在內存(堆)中被創建出來, 他就無法被修改。特別要注意的是,String 類的所有方法都沒有改變字符串本身的值, 都是返回了一個新的對象。
如果你需要一個可修改的字符串, 應該使用 StringBuffer 或者 StringBuilder。否則會有大量時間浪費在垃圾回收上, 因為每次試圖修改都有新的 string 對象被創建出來 ?
JDK 6 和 JDK 7 中 substring 的原理及區別 ?
調用 substring()時發生了什么??
你可能知道, 因為 x 是不可變的, 當使用 x.substring(1,3)對 x 賦值的時候, 它會指向一個全新的字符串:?
JDK 6 中的 substring ?
String 是通過字符數組實現的。在 jdk 6 中, String 類包含三個成員變量:char value[], int offset, int count。他們分別用來存儲真正的字符數組, 數組的第一個位置索引以及字符串中包含的字符個數。?
String 是通過字符數組實現的。在 jdk 6 中, String 類包含三個成員變量:char value[], int offset, int count。他們分別用來存儲真正的字符數組, 數組的第一個位置索引以及字符串中包含的字符個數。
JDK 6 中的 substring 導致的問題 ? ?
如果你有一個很長很長的字符串, 但是當你使用 substring 進行切割的時候你只需要很短的一段。這可能導致性能問題, 因為你需要的只是一小段字符序列, 但是你卻引用了整個字符串( 因為這個非常長的字符數組一直在被引用, 所以無法被回收, 就可能導致內存泄露) 。在 JDK 6 中, 一般用以下方式來解決該問題, 原理其實就是生成一個新的字符串并引用他。?
內存泄露:在計算機科學中, 內存泄漏指由于疏忽或錯誤造成程序未能釋放已經不再使用的內存。內存泄漏并非指內存在物理上的消失, 而是應用程序分配某段內存后, 由于設計錯誤, 導致在釋放該段內存之前就失去了對該段內存的控制, 從而造成了內存的浪費 ?
JDK 7 中的 substring ?
上面提到的問題, 在 jdk 7 中得到解決。在 jdk 7 中, substring 方法會在堆內存中創建一個新的數組。?
以上是 JDK 7 中的 subString 方法, 其使用 new String 創建了一個新字符串, 避免對老字符串的引用。從而解決了內存泄露問題。
所以, 如果你的生產環境中使用的 JDK 版本小于 1.7, 當你使用 String 的 subString方法時一定要注意, 避免內存泄露。?
replaceFirst、 replaceAll、 replace 區別 ?
replace(CharSequence target, CharSequence replacement) , 用replacement 替換所有的 target, 兩個參數都是字符串。
replaceAll(String regex, String replacement) , 用 replacement 替換所有的regex 匹配項, regex 很明顯是個正則表達式, replacement 是字符串。
replaceFirst(String regex, String replacement) , 基本和 replaceAll 相同, 區別是只替換第一個匹配項。可以看到, 其中 replaceAll 以及 replaceFirst 是和正則表達式有關的, 而 replace 和正則表達式無關。?
http://www.51gjie.com/java/771.html ?
String 對“ +” 的重載 ?
1、 String s = "a" + "b", 編譯器會進行常量折疊(因為兩個都是編譯期常量, 編譯期可知), 即變成 String s = "ab ?
2、 對于能夠進行優化的(String s = "a" + 變量 等)用 StringBuilder 的 append()方法替代, 最后調用 toString() 方法 (底層就是一個 new String()) ?
字符串拼接的幾種方式和區別 ?
字符串不變性與字符串拼接 ?
其實, 所有的所謂字符串拼接, 都是重新生成了一個新的字符串。下面一段字符串拼接代碼:String s = "abcd";s = s.concat("ef");其實最后我們得到的 s 已經是一個新的字符串了。?
使用+拼接字符串 ?
運算符重載:在計算機程序設計中, 運算符重載( 英語:operator overloading) 是多態的一種。運算符重載, 就是對已有的運算符重新進行定義, 賦予其另一種功能, 以適應不同的數據類型。?
語法糖:語法糖( Syntactic sugar), 也譯為糖衣語法, 是由英國計算機科學家彼得· 蘭丁發明的一個術語, 指計算機語言中添加的某種語法, 這種語法對語言的功能沒有影響, 但是更方便程序員使用。語法糖讓程序更加簡潔, 有更高的可讀性。?
Concat ?
StringBuffer ?
StringBuilder
StringUtils.join ?
除了 JDK 中內置的字符串拼接方法, 還可以使用一些開源類庫中提供的字符串拼接方法名, 如 apache.commons 中提供的 StringUtils 類, 其中的 join 方法可以拼接字符串。String wechat = "Hollis";String introduce = "每日更新 Java 相關技術文章";System.out.println(StringUtils.join(wechat, ",", introduce)); ?
以上就是比較常用的五種在 Java 種拼接字符串的方式, 那么到底哪種更好用呢?為什么 Java 開發手冊中不建議在循環體中使用+進行字符串拼接呢??
使用+拼接字符串的實現原理 ?
通過查看反編譯以后的代碼, 我們可以發現, 原來字符串常量在拼接過程中, 是將String 轉成了 StringBuilder 后, 使用其 append 方法進行處理的。
那 么 也 就 是 說 , J a v a 中 的 + 對 字 符 串 的 拼 接 , 其 實 現 原 理 是 使 用StringBuilder.append ? ?
concat 是如何實現的 ?
concat 方法的源代碼 ?
這段代碼首先創建了一個字符數組, 長度是已有字符串和待拼接字符串的長度之和, 再把兩個字符串的值復制到新的字符數組中, 并使用這個字符數組創建一個新的 String 對象并返回。
通過源碼我們也可以看到, 經過 concat 方法, 其實是 new 了一個新的 String, 這也就呼應到前面我們說的字符串的不變性問題上了。?
StringBuffer 和 StringBuilder ?
append 會直接拷貝字符到內部的字符數組中, 如果字符數組長度不夠, 會進行擴展。
StringBuffer 和 StringBuilder 類似, 最大的區別就是 StringBuffer 是線程安全的,看一下 StringBuffer 的 append 方法。?
StringUtils.join 是如何實現的 ?
通過查看 StringUtils.join 的源代碼, 我們可以發現, 其實他也是通過 StringBuilder來實現的。?
效率比較 ?
從結果可以看出, 用時從短到長的對比是:StringBuilderStringBuffer 在 StringBuilder 的基礎上, 做了同步處理, 所以在耗時上會相對多一些。
StringUtils.join 也是使用了 StringBuilder, 并且其中還是有很多其他操作, 所以耗時較長, 這個也容易理解。其實 StringUtils.join 更擅長處理字符串數組或者列表的拼接。
那么問題來了, 前面我們分析過, 其實使用+拼接字符串的實現原理也是使用的StringBuilder, 那為什么結果相差這么多, 高達 1000 多倍呢??
我們可以看到反編譯后的代碼, 在 for 循環中, 每次都是 new 了一個 StringBuilder,然后再把 String 轉成 StringBuilder, 再進行 append。而頻繁的新建對象當然要耗費很多時間了, 不僅僅會耗費時間, 頻繁的創建對象, 還會造成內存資源的浪費。
所以, Java 開發手冊建議:循環體內, 字符串的連接方式, 使用 StringBuilder 的append 方法進行擴展。而不要使用+。?
總結
本文介紹了什么是字符串拼接, 雖然字符串是不可變的, 但是還是可以通過新建字符串的方式來進行字符串的拼接。常用的字符串拼接方式有五種, 分別是使用+、 使用 concat、 使用 StringBuilder、 使用 StringBuffer 以及使用 StringUtils.join。由于字符串拼接過程中會創建新的對象, 所以如果要在一個循環體中進行字符串拼接,就要考慮內存問題和效率問題。
因此, 經過對比, 我們發現, 直接使用 StringBuilder 的方式是效率最高的。因為StringBuilder 天生就是設計來定義可變字符串和字符串的變化操作的。但是, 還要強調的是:1、 如果不是在循環體中進行字符串拼接的話, 直接使用+就好了。2 、 如 果 在 并 發 場 景 中 進 行 字 符 串 拼 接 的 話 , 要 使 用 St r i n g B u f f e r 來 代 替StringBuilder。?
String.valueOf 和 Integer.toString 的區別 ?
String.valueOf 和 Integer.toString 的區別 ?
1.int i = 5;2.String i1 = "" + i;3.String i2 = String.valueOf(i);4.String i3 = Integer.toString(i);第三行和第四行沒有任何區別, 因為 String.valueOf(i)也是調用 Integer.toString(i)來實現的。第二行代碼其實是 String i1 = (new StringBuilder()).append(i).toString();, 首先創建一個 StringBuilder 對象, 然后再調用 append 方法, 再調用 toString 方法。?
字符串池 ?
在 JVM 中, 為了減少相同的字符串的重復創建, 為了達到節省內存的目的。會單獨開辟一塊內存, 用于保存字符串常量, 這個內存區域被叫做字符串常量池 ?
當代碼中出現雙引號形式( 字面量) 創建字符串對象時, JVM 會先對這個字符串進行檢查, 如果字符串常量池中存在相同內容的字符串對象的引用, 則將這個引用返回;否則,創建新的字符串對象, 然后將這個引用放入字符串常量池, 并返回該引用 ?
這種機制, 就是字符串駐留或池化 ?
字符串常量池的位置 ?
在 JDK 7 以前的版本中, 字符串常量池是放在永久代中的。
因為按照計劃, JDK 會在后續的版本中通過元空間來代替永久代, 所以首先在 JDK7 中, 將字符串常量池先從永久代中移出, 暫時放到了堆內存中。在 JDK 8 中, 徹底移除了永久代, 使用元空間替代了永久代, 于是字符串常量池再次從堆內存移動到永久代中 ?
Class 常量池 ?
談到常量池, 在 Java 體系中, 共用三種常量池。分別是字符串常量池、 Class 常量池和運行時常量池。?
Class 常量池可以理解為是 Class 文件中的資源倉庫。Class 文件中除了包含類的版本、 字段、 方法、 接口等描述信息外, 還有一項信息就是常量池(constant pool table),用于存放編譯器生成的各種字面量(Literal)和符號引用(Symbolic References)。?
常量池中有什么 ?
字面量 ?
用于表達源代碼中一個固定值的表示法( notation) ?
字面量只可以右值出現, 所謂右值是指等號右邊的值, 如:int a=123 這里的 a 為左值, 123 為右值。在這個例子中 123 就是字面量 ?
符號引用 ?
符號引用是編譯原理中的概念, 是相對于直接引用來說的。主要包括了以下三類常量:* 類和接口的全限定名 * 字段的名稱和描述符 * 方法的名稱和描述符 ?
Class 常量池有什么用 ?
Class 常量池是 Class 文件中的資源倉庫, 其中保存了各種常量。而這些常量都是開發者定義出來, 需要在程序的運行期使用的 ?
Class 是用來保存常量的一個媒介場所, 并且是一個中間場所。在 JVM 真的運行時, 需要把常量池中的常量加載到內存中 ?
運行時常量池 ?
它包括了若干種不同的常量:從編譯期可知的數值字面量到必須運行期解析后才能獲得的方法或字段引用。運行時常量池扮演了類似傳統語言中符號(SymbolTable)的角色,不過它存儲數據范圍比通常意義上的符號表要更為廣泛 ?
每一個運行時常量池都分配在 Java 虛擬機的方法區之中, 在類和接口被加載到虛擬機后, 對應的運行時常量池就被創建出來 ?
intern ?(運行時)
在 JVM 中, 為了減少相同的字符串的重復創建, 為了達到節省內存的目的。會單獨開辟一塊內存, 用于保存字符串常量, 這個內存區域被叫做字符串常量池。?
intern 的功能很簡單:在每次賦值的時候使用 String 的 intern 方法, 如果常量池中有相同值, 就會重復使用該對象, 返回對象引用。?
String 的長度限制 ?
那么, 明明 String 的構造函數指定的長度是可以支持 2147483647(2^31 - 1)的, 為什么像以上形式定義的時候無法編譯呢?其實, 形如 String s = "xxx";定義 String 的時候, xxx 被我們稱之為字面量, 這種字面量在編譯之后會以常量的形式進入到 Class 常量池 ?
常量池限制 ?
字符串有長度限制, 在編譯期, 要求字符串常量池中的常量不能超過 65535, 并且在javac 執行過程中控制了最大值為 65534。在運行期, 長度不能超過 Int 的范圍, 否則會拋異常 ?
運行期限制 ?
前面提到的這種 String 長度的限制是編譯期的限制, 也就是使用 String s= “ ” ;這種字面值方式定義的時候才會有的限制。
那么。String 在運行期有沒有限制呢, 答案是有的, 就是我們前文提到的那個 Integer.MAX_VALUE , 這個值約等于 4G, 在運行期, 如果 String 的長度超過這個范圍, 就可能會拋出異常 ?
總結
字符串有長度限制, 在編譯期, 要求字符串常量池中的常量不能超過 65535, 并且在javac 執行過程中控制了最大值為 65534。在運行期, 長度不能超過 Int 的范圍, 否則會拋異常。
最后, 這個知識點 , 我錄制了視頻(https://www.bilibili.com/video/BV1uK4y1t7H1/),
其中有關于如何進行實驗測試、如何查閱 Java 規范以及如何對 javac 進行 deubg 的技巧。歡迎進一步學習。?
這是筆記!
作者 Hollis:https://github.com/hollischuang
總結
以上是生活随笔為你收集整理的stringbuilder寻找字符串位置可能存在多个 java_Java 语言基础amp;String的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 今年618最淡定的手机品牌 赵明:荣耀没
- 下一篇: 《网络剧片发行许可证》明日起全面发放:统