java基礎(chǔ)教學(xué)欄位介紹洞察String字串
#推薦(免費(fèi)):java基礎(chǔ)教學(xué)
#實(shí)作原理
##在Java6 以及在先前的版本中,String 物件是對(duì)char 陣列進(jìn)行了封裝實(shí)作的對(duì)象,主要有四個(gè)成員變數(shù):char 陣列、偏移量offset、字元數(shù)量count、雜湊值hash。 從 Java7 版本開(kāi)始到 Java8 版本,String 類別中不再有 offset 和 count 兩個(gè)變數(shù)了。這樣的好處是 String 物件佔(zhàn)用的記憶體稍微少了些。 從 Java9 版本開(kāi)始,將 char[]字段改為 byte[]字段,又維護(hù)了一個(gè)新的屬性 coder,它是一個(gè)編碼格式的標(biāo)識(shí)。 一個(gè) char 字元佔(zhàn) 16 位,2 個(gè)位元組。這個(gè)情況下,儲(chǔ)存單字節(jié)編碼內(nèi)的字元(佔(zhàn)一個(gè)位元組的字元)就顯得非常浪費(fèi)。 JDK1.9 的 String 類別為了節(jié)省記憶體空間,於是使用了佔(zhàn) 8 位,1 個(gè)位元組的 byte 陣列來(lái)存放字串。 而新屬性 coder 的作用是,在計(jì)算字串長(zhǎng)度或使用 indexOf()函數(shù)時(shí),我們需要根據(jù)這個(gè)字段,判斷如何計(jì)算字串長(zhǎng)度。 coder 屬性預(yù)設(shè)有 0 和 1 兩個(gè)值,0 代表 Latin-1(單字節(jié)編碼),1 代表 UTF-16。如果 String 判斷字串只包含了 Latin-1,則 coder 屬性值為 0,反之則為 1。不可變
查看String類別的程式碼可以發(fā)現(xiàn),String類別被final關(guān)鍵字修飾,因此這個(gè)類別不能被繼承,並且String類別裡面的變數(shù)char 陣列也被final 修飾了,因此String物件不能被修改。 String物件不可變主要有以下幾個(gè)優(yōu)點(diǎn):第一,保證 String 物件的安全性。假設(shè) String 物件是可變的,那麼 String 物件將可能被惡意修改。 第二,保證 hash 屬性值不會(huì)頻繁變更,確保了唯一性,使得類似 HashMap 容器才能實(shí)現(xiàn)對(duì)應(yīng)的 key-value 快取功能。 第三,可以實(shí)作字串常數(shù)池。 在Java 中,通常有兩種創(chuàng)建字串物件的方式:第一種是透過(guò)字串常數(shù)的方式創(chuàng)建,如String str = "abc"。
String str = new String("abc")。
String str = new String("abc") 這種方式,首先在編譯類別檔案時(shí),」abc」常數(shù)字串將會(huì)放入到常數(shù)結(jié)構(gòu)中,在類別載入時(shí),「abc」將會(huì)在常數(shù)池中建立;其次,在呼叫new時(shí),JVM 指令將會(huì)呼叫String 的建構(gòu)函數(shù),String 物件中的char 陣列將會(huì)引用
常數(shù)池中」abc」字符串的char 數(shù)組,在堆內(nèi)存中創(chuàng)建一個(gè)String 對(duì)象;最後,str 將引用String 對(duì)象,String對(duì)象的引用跟常數(shù)池中”abc”字符串的引用是不一樣的。
String str = new String("abc"),變數(shù)str指向的是String物件的儲(chǔ)存位址,也就是說(shuō) str 並不是對(duì)象,而只是一個(gè)物件參考。
字串拼接
#常數(shù)相加##String?str?=?"ab"?+?"cd"?+?"ef";
查看編譯後的字節(jié)碼
0?ldc?#2?<abcdef>2?astore_13?return
可以發(fā)現(xiàn)編譯器將程式碼最佳化成如下所示
String?str=?"abcdef";
變數(shù)相加##
String?a?=?"ab";String?b?=?"cd";String?c?=?a?+?b;查看編譯後的字節(jié)碼
?0?ldc?#2?<ab> ?2?astore_1?3?ldc?#3?<cd> ?5?astore_2?6?new?#4?<java/lang/StringBuilder> ?9?dup10?invokespecial?#5?<java/lang/StringBuilder.<init>>13?aload_114?invokevirtual?#6?<java/lang/StringBuilder.append>17?aload_218?invokevirtual?#6?<java/lang/StringBuilder.append>21?invokevirtual?#7?<java/lang/StringBuilder.toString>24?astore_325?return可以發(fā)現(xiàn),Java在進(jìn)行字串相加的時(shí)候,底層使用的是StringBuilder,程式碼被最佳化成如下所示:
String?c?=?new?StringBuilder().append("ab").append("cd").toString();
String.intern
String?a?=?new?String("abc").intern();String?b?=?new?String("abc").intern();System.out.print(a?==?b);輸出結(jié)果:
true在字串
常數(shù)
中,預(yù)設(shè)會(huì)將物件放入常量池。例如:String a = "123"
在字符串變量中,對(duì)象是會(huì)創(chuàng)建在堆內(nèi)存中,同時(shí)也會(huì)在常量池中創(chuàng)建一個(gè)字符串對(duì)象,String 對(duì)象中的 char 數(shù)組將會(huì)引用常量池中的 char 數(shù)組,并返回堆內(nèi)存對(duì)象引用。例如:String b = new String("abc")
如果調(diào)用 intern 方法,會(huì)去查看字符串常量池中是否有等于該對(duì)象的字符串的引用,如果沒(méi)有,在 JDK1.6 版本中會(huì)復(fù)制堆中的字符串到常量池中,并返回該字符串引用,堆內(nèi)存中原有的字符串由于沒(méi)有引用指向它,將會(huì)通過(guò)垃圾回收器回收。
在 JDK1.7 版本以后,由于常量池已經(jīng)合并到了堆中,所以不會(huì)再?gòu)?fù)制具體字符串了,只是會(huì)把首次遇到的字符串的引用添加到常量池中;如果有,就返回常量池中的字符串引用。
下面開(kāi)始分析上面的代碼塊:
在一開(kāi)始字符串”abc”會(huì)在加載類時(shí),在常量池中創(chuàng)建一個(gè)字符串對(duì)象。
創(chuàng)建 a 變量時(shí),調(diào)用 new Sting() 會(huì)在堆內(nèi)存中創(chuàng)建一個(gè) String 對(duì)象,String 對(duì)象中的 char 數(shù)組將會(huì)引用常量池中字符串。在調(diào)用 intern 方法之后,會(huì)去常量池中查找是否有等于該字符串對(duì)象的引用,有就返回常量池中的字符串引用。
創(chuàng)建 b 變量時(shí),調(diào)用 new Sting() 會(huì)在堆內(nèi)存中創(chuàng)建一個(gè) String 對(duì)象,String 對(duì)象中的 char 數(shù)組將會(huì)引用常量池中字符串。在調(diào)用 intern 方法之后,會(huì)去常量池中查找是否有等于該字符串對(duì)象的引用,有就返回常量池中的字符串引用。
而在堆內(nèi)存中的兩個(gè)String對(duì)象,由于沒(méi)有引用指向它,將會(huì)被垃圾回收。所以 a 和 b 引用的是同一個(gè)對(duì)象。
如果在運(yùn)行時(shí),創(chuàng)建字符串對(duì)象,將會(huì)直接在堆內(nèi)存中創(chuàng)建,不會(huì)在常量池中創(chuàng)建。所以動(dòng)態(tài)創(chuàng)建的字符串對(duì)象,調(diào)用 intern 方法,在 JDK1.6 版本中會(huì)去常量池中創(chuàng)建運(yùn)行時(shí)常量以及返回字符串引用,在 JDK1.7 版本之后,會(huì)將堆中的字符串常量的引用放入到常量池中,當(dāng)其它堆中的字符串對(duì)象通過(guò) intern 方法獲取字符串對(duì)象引用時(shí),則會(huì)去常量池中判斷是否有相同值的字符串的引用,此時(shí)有,則返回該常量池中字符串引用,跟之前的字符串指向同一地址的字符串對(duì)象。
以一張圖來(lái)總結(jié) String 字符串的創(chuàng)建分配內(nèi)存地址情況:
使用 intern 方法需要注意的一點(diǎn)是,一定要結(jié)合實(shí)際場(chǎng)景。因?yàn)槌A砍氐膶?shí)現(xiàn)是類似于一個(gè) HashTable 的實(shí)現(xiàn)方式,HashTable 存儲(chǔ)的數(shù)據(jù)越大,遍歷的時(shí)間復(fù)雜度就會(huì)增加。如果數(shù)據(jù)過(guò)大,會(huì)增加整個(gè)字符串常量池的負(fù)擔(dān)。
判斷字符串是否相等
//?運(yùn)行環(huán)境?JDK1.8String?str1?=?"abc";String?str2?=?new?String("abc");String?str3=?str2.intern();System.out.println(str1==str2);?//?falseSystem.out.println(str2==str3);?//?falseSystem.out.println(str1==str3);?//?true
//?運(yùn)行環(huán)境?JDK1.8String?s1?=?new?String("1")?+?new?String("1");s1.intern();String?s2?=?"11";System.out.println(s1?==?s2);?//?true?,?如果不執(zhí)行1.intern(),則返回false
String s1 = new String("1") + new String("1")
會(huì)在堆中組合一個(gè)新的字符串對(duì)象"11"
,在s1.intern()
之后,由于常量池中沒(méi)有該字符串的引用,所以常量池中生成一個(gè)堆中字符串"11"
的引用,此時(shí)String s2 = "11"
返回的是堆字符串"11"
的引用,所以s1==s2
。
在JDK1.7版本以及之后的版本運(yùn)行以下代碼,你會(huì)發(fā)現(xiàn)結(jié)果為true,在JDK1.6版本運(yùn)行的結(jié)果卻為false:
String?s1?=?new?String("1")?+?new?String("1");System.out.println(?s1.intern()==s1);
StringBuilder與StringBuffer
由于String的值是不可變的,這就導(dǎo)致每次對(duì)String的操作都會(huì)生成新的String對(duì)象,這樣不僅效率低下,而且大量浪費(fèi)有限的內(nèi)存空間。
和 String 類不同的是,StringBuffer 和 StringBuilder 類的對(duì)象能夠被多次的修改,并且不產(chǎn)生新的對(duì)象。
StringBuilder 類在 Java 5 中被提出,它和 StringBuffer 之間的最大不同在于 StringBuilder 的方法不是線程安全的(不能同步訪問(wèn))。
由于 StringBuilder 相較于 StringBuffer 有速度優(yōu)勢(shì),所以多數(shù)情況下建議使用 StringBuilder 類。然而在應(yīng)用程序要求線程安全的情況下,則必須使用 StringBuffer 類。
以上是洞察 String字串的詳細(xì)內(nèi)容。更多資訊請(qǐng)關(guān)注PHP中文網(wǎng)其他相關(guān)文章!

熱AI工具

Undress AI Tool
免費(fèi)脫衣圖片

Undresser.AI Undress
人工智慧驅(qū)動(dòng)的應(yīng)用程序,用於創(chuàng)建逼真的裸體照片

AI Clothes Remover
用於從照片中去除衣服的線上人工智慧工具。

Clothoff.io
AI脫衣器

Video Face Swap
使用我們完全免費(fèi)的人工智慧換臉工具,輕鬆在任何影片中換臉!

熱門(mén)文章

熱工具

記事本++7.3.1
好用且免費(fèi)的程式碼編輯器

SublimeText3漢化版
中文版,非常好用

禪工作室 13.0.1
強(qiáng)大的PHP整合開(kāi)發(fā)環(huán)境

Dreamweaver CS6
視覺(jué)化網(wǎng)頁(yè)開(kāi)發(fā)工具

SublimeText3 Mac版
神級(jí)程式碼編輯軟體(SublimeText3)