日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問(wèn) 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) > 编程资源 > 编程问答 >内容正文

编程问答

java字符串底层实现_「JAVA」细述合理创建字符串,分析字符串的底层存储,你不该错过...

發(fā)布時(shí)間:2023/12/3 编程问答 36 豆豆
生活随笔 收集整理的這篇文章主要介紹了 java字符串底层实现_「JAVA」细述合理创建字符串,分析字符串的底层存储,你不该错过... 小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

Java基礎(chǔ)之字符串操作——String

字符串

什么是字符串?如果直接按照字面意思來(lái)理解就是多個(gè)字符連接起來(lái)組合成的字符序列。為了更好的理解以上的理論,我們先來(lái)解釋下字符序列,字符序列:把多個(gè)字符按照一定的順序排列起來(lái);而字符序列就是作為字符串的內(nèi)容而存在的。所以可以把字符串理解為:把多個(gè)字符按照一定的順序排列起來(lái)而構(gòu)成的排列組合。

如果還是不好理解,沒(méi)有關(guān)系,我還有法寶。我們可以用烤串來(lái)比喻說(shuō)明,可以把字符串看作是烤串,烤串上的每一塊肉都相當(dāng)于是一個(gè)字符。把一塊塊肉按照肥瘦相間的順序排列并串起來(lái)便成了我們吃的烤串,同理,把多個(gè)字符按照一定的順序“串”起來(lái)就構(gòu)成了字符串。

字符串的分類,字符串分為可變的字符串和不可變的字符串兩種;這里的不可變與可變指的是字符串的對(duì)象還是不是同一個(gè),會(huì)不會(huì)因?yàn)樽址畬?duì)象內(nèi)容的改變而創(chuàng)建新的對(duì)象。

不可變的字符串:當(dāng)字符串對(duì)象創(chuàng)建完畢之后,該對(duì)象的內(nèi)容(上述的字符序列)是不能改變的,一旦內(nèi)容改變就會(huì)創(chuàng)建一個(gè)新的字符串對(duì)象;Java中的String類的對(duì)象就是不可變的。

可變的字符串:StringBuilder類和StringBuffer類的對(duì)象就是可變的;當(dāng)對(duì)象創(chuàng)建完畢之后,該對(duì)象的內(nèi)容發(fā)生改變時(shí)不會(huì)創(chuàng)建新的對(duì)象,也就是說(shuō)對(duì)象的內(nèi)容可以發(fā)生改變,當(dāng)對(duì)象的內(nèi)容發(fā)生改變時(shí),對(duì)象保持不變,還是同一個(gè)。

String 類

String類表示不可變的字符串,當(dāng)前String類對(duì)象創(chuàng)建完畢之后,該對(duì)象的內(nèi)容(字符序列)是不變的,因?yàn)閮?nèi)容一旦改變就會(huì)創(chuàng)建一個(gè)一個(gè)新的對(duì)象。

String對(duì)象的創(chuàng)建:

方式一:通過(guò)字面量賦值創(chuàng)建,String s1 = "laofu";需要注意這里是雙引號(hào):"",區(qū)別與字符char類型的單引號(hào):'';

方式二:通過(guò)構(gòu)造器創(chuàng)建, String s2 = new String("laofu");;

以上兩種創(chuàng)建方式的對(duì)象在JVM中又是如何分布的呢? 分別有什么區(qū)別呢?

方式一和方式二在JVM中又是如何分布?

上圖中的常量池:用于存儲(chǔ)常量的地方內(nèi)存區(qū)域,位于方法區(qū)中。常量池又分為編譯常量池和運(yùn)行常量池兩種:

編譯常量池:當(dāng)把字節(jié)碼加載進(jìn)JVM的時(shí)候,其中存儲(chǔ)的是字節(jié)碼的相關(guān)信息(如:當(dāng)前執(zhí)行到的字節(jié)碼行號(hào)等)。

運(yùn)行常量池:其中存儲(chǔ)的是代碼中的常量數(shù)據(jù)。

方式一和方式二有何不同?

方式一:String s1 = "laofu";有可能只創(chuàng)建一個(gè)String對(duì)象,也有可能創(chuàng)建不創(chuàng)建String對(duì)象;如果在常量池中已經(jīng)存在"laofu",那么對(duì)象s1會(huì)直接引用,不會(huì)創(chuàng)建新的String對(duì)象;否則,會(huì)先在常量池先創(chuàng)建常量"laofu"的內(nèi)存空間,然后再引用。

方式二:String s2 = new String("laofu");最多會(huì)創(chuàng)建兩個(gè)String對(duì)象,最少創(chuàng)建一個(gè)String對(duì)象。可使用new關(guān)鍵字創(chuàng)建對(duì)象是會(huì)在堆空間創(chuàng)建內(nèi)存區(qū)域,這是第一個(gè)對(duì)象;然后對(duì)象中的字符串字面量可能會(huì)創(chuàng)建第二個(gè)對(duì)象,而第二個(gè)對(duì)象如方式一中所描述的那樣,是有可能會(huì)不被創(chuàng)建的,所以至少創(chuàng)建一個(gè)String對(duì)象。

字符串的本質(zhì),字符串在底層其實(shí)就是char[],char表示一個(gè)字符,比如:

String str = "laofu";

等價(jià)于

char[] cs = new char[]{'l','a','o','f','u'};

String對(duì)象的空值:

對(duì)象引用為空,即:String s1 = null;此時(shí)s1沒(méi)有初始化,也在JVM中沒(méi)有分配內(nèi)存空間。

對(duì)象內(nèi)容為空字符串, 比如:String s2 = "";此時(shí)對(duì)象s2已經(jīng)初始化,值為"",JVM已經(jīng)為其分配內(nèi)存空間。

字符串的比較:使用"=="和"equals"會(huì)有不同效果,詳情在之前的文章中分享過(guò):[JAVA] 只知對(duì)象屬性,不知類屬性?就算類答應(yīng),static都不答應(yīng)

使用==號(hào):用于比較對(duì)象引用的內(nèi)存地址是否相同。

使用equals方法:在Object類中和==號(hào)相同,但在自定義類中,建議覆蓋equals方法去實(shí)現(xiàn)比較自己內(nèi)容的細(xì)節(jié);由于String類覆蓋已經(jīng)覆蓋了equals方法,所以其比較的是字符內(nèi)容。

所以可以這樣來(lái)判斷字符串非空:

對(duì)象引用不能為空:s1 != null;;

字符內(nèi)容不能為空字符串(""):"".equals(s1);;

如果上述兩個(gè)條件都滿足,說(shuō)明字符串確實(shí)為空!

字符串拼接:Java中的字符串可以通過(guò)是"+"實(shí)現(xiàn)拼接,那么代碼中字符串拼接在JVM中又是如何處理的呢?我們通過(guò)一個(gè)例子說(shuō)明:通過(guò)比較拼接字符串代碼編譯前后的代碼來(lái)查看JVM對(duì)字符串拼接的處理。

通過(guò)上述例子不難發(fā)現(xiàn),JVM會(huì)對(duì)字符串拼接做一些優(yōu)化操作,如果字符串字面量之間的拼接,無(wú)論有多少個(gè)字符串,JVM都會(huì)一樣的處理;如果是對(duì)象之間拼接,或者是對(duì)象和字面量之間的拼接,亦或是方法執(zhí)行結(jié)果參與拼接,String內(nèi)部會(huì)使用StringBuilder先來(lái)獲取對(duì)象的值,然后使用append方法來(lái)執(zhí)行拼接。由此可以總結(jié)得出:

使用字符串字面量創(chuàng)建的字符串,也就是單獨(dú)使用""引號(hào)創(chuàng)建的字符串都是直接量,在編譯期就會(huì)將其存儲(chǔ)到常量池中;

使用new String("")創(chuàng)建的對(duì)象會(huì)存儲(chǔ)到堆內(nèi)存中,在運(yùn)行期才創(chuàng)建;

使用只包含直接量的字符串連接符如"aa" + "bb"創(chuàng)建的也是直接量,這樣的字符串在編譯期就能確定,所以也會(huì)存儲(chǔ)到常量池中;

使用包含String直接量的字符串表達(dá)式(如"aa" + s1)創(chuàng)建的對(duì)象是運(yùn)行期才創(chuàng)建的,對(duì)象存儲(chǔ)在堆中,因?yàn)槠涞讓邮莿?chuàng)新了StringBuilder對(duì)象來(lái)實(shí)現(xiàn)拼接的;

5. 無(wú)論是使用變量,還是調(diào)用方法來(lái)連接字符串,都只能在運(yùn)行期才能確定變量的值和方法的返回值,不存在編譯優(yōu)化操作。

String 的常用API

這里列舉了一些常用String API,更多的可以查閱jdk使用手冊(cè),做Java一定得學(xué)會(huì)查閱jdk手冊(cè)。

String 的創(chuàng)建和轉(zhuǎn)換:

byte[] getBytes():把字符串轉(zhuǎn)換為byte數(shù)組。

char[] toCharArray():把字符串轉(zhuǎn)換為char數(shù)組。

String(byte[] bytes):把byte數(shù)組轉(zhuǎn)換為字符串。

String(char[] value):把char數(shù)組轉(zhuǎn)換為字符串。

獲取字符串信息

int length() :返回此字符串的長(zhǎng)度。

char charAt(int index) :返回指定索引處的 char值。

int indexOf(String str) :返回指定字符串在此字符串中首次(從最左邊算起)出現(xiàn)處的索引。

int lastIndexOf(String str) :返回指定字符串在此字符串中最后(最右邊算起)出現(xiàn)處的索引。

字符串比較判斷

boolean equals(Object anObject): 將此字符串與指定的對(duì)象比較。

boolean equalsIgnoreCase(String anotherString): 將此 String 與另一個(gè) String 做忽略大小寫的比較。

boolean contentEquals(CharSequence cs): 將此字符串與指定的 CharSequence 比較,比較的是內(nèi)容;

ps:String類是現(xiàn)實(shí)了CharSequence(字符序列)接口的。

字符串大小寫轉(zhuǎn)換:調(diào)用方法的字符串就是當(dāng)前字符串

String toUpperCase(): 把當(dāng)前字符串轉(zhuǎn)換為大寫

String toLowerCase(): 把當(dāng)前字符串轉(zhuǎn)換為小寫

StringBuilder/StringBuffer

先來(lái)分別使用String/StringBuilder/StringBuffer來(lái)拼接30000次字符串,對(duì)比各自損耗的時(shí)間,經(jīng)過(guò)測(cè)試發(fā)現(xiàn):

String做字符串拼接的時(shí)候,耗時(shí)最高,性能極低,原因是String內(nèi)容是不可變的,每次內(nèi)容改變都會(huì)在內(nèi)存中創(chuàng)建新的對(duì)象。

性能最好的是StringBuilder,其次是StringBuffer,最后是String。StringBuilder和StringBuffer區(qū)別并不是很大,也有可能是測(cè)試次數(shù)還不夠吧。感興趣的小伙伴可以增加拼接次數(shù)來(lái)看看。代碼很簡(jiǎn)單,就不展示出來(lái)了。

所以在開(kāi)發(fā)中拼接字符串時(shí),優(yōu)先使用StringBuffer/StringBuilder,不到萬(wàn)不得已,不要輕易使用String。

StringBuilder以及StringBuffer的區(qū)別

StringBuffer和StringBuilder都表示可變的字符串,兩種’的功能方法都是相同的。但唯一的區(qū)別:

StringBuffer:StringBuffer中的方法都使用了synchronized修飾符,表示同步操作,在多線程并發(fā)的時(shí)候可以保證線程安全,但在保證線程安全的時(shí)候,對(duì)其性能有一定影響,會(huì)降低其性能。

StringBuilder:StringBuilder中的方法都沒(méi)有使用了synchronized修飾符,線程不安全,正因?yàn)槿绱?#xff0c;其性能較高。

對(duì)并發(fā)安全沒(méi)有很高要求的情況下,建議使用StringBuilder,因?yàn)槠湫阅芎芨摺O襁@樣的情況會(huì)較多些。使用StringBuilder無(wú)參數(shù)的構(gòu)造器,在底層創(chuàng)建了一個(gè)長(zhǎng)度為16的char數(shù)組:

此時(shí)該數(shù)組只能存儲(chǔ)16個(gè)字符,如果超過(guò)了16個(gè)字符,會(huì)自動(dòng)擴(kuò)容(創(chuàng)建長(zhǎng)度更大的數(shù)組,再把之前的數(shù)組拷貝到新數(shù)組),此時(shí)性能極低;如果事先知道大概需要存儲(chǔ)多少字符,可以通過(guò)構(gòu)造器來(lái)設(shè)置字符的初始值:

//創(chuàng)建一個(gè)長(zhǎng)度為80的char數(shù)組.

new StringBuilder(80);

StringBuilder的常用方法:

append(Object val):追加任意類型數(shù)據(jù)到當(dāng)前StringBuilder對(duì)象中。

StringBuilder deleteCharAt(int index) :刪除字符串中指定位置的字符。

完結(jié)。雖然老夫不正經(jīng),但老夫一身的才華

總結(jié)

以上是生活随笔為你收集整理的java字符串底层实现_「JAVA」细述合理创建字符串,分析字符串的底层存储,你不该错过...的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。

如果覺(jué)得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。