溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

String字符串的示例分析

發布時間:2020-12-29 14:33:01 來源:億速云 閱讀:193 作者:小新 欄目:編程語言

這篇文章主要介紹String字符串的示例分析,文中介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們一定要看完!

實現原理

在 Java6 以及之前的版本中,String 對象是對 char 數組進行了封裝實現的對象,主要有四個成員變量:char 數組、偏移量 offset、字符數量 count、哈希值 hash。

從 Java7 版本開始到 Java8 版本,String 類中不再有 offset 和 count 兩個變量了。這樣的好處是 String 對象占用的內存稍微少了些。

從 Java9 版本開始,將 char[]字段改為了 byte[]字段,又維護了一個新的屬性 coder,它是一個編碼格式的標識。

一個 char 字符占 16 位,2 個字節。這個情況下,存儲單字節編碼內的字符(占一個字節的字符)就顯得非常浪費。JDK1.9 的 String 類為了節約內存空間,于是使用了占 8 位,1 個字節的 byte 數組來存放字符串。

而新屬性 coder 的作用是,在計算字符串長度或者使用 indexOf()函數時,我們需要根據這個字段,判斷如何計算字符串長度。coder 屬性默認有 0 和 1 兩個值,0 代表 Latin-1(單字節編碼),1 代表 UTF-16。如果 String 判斷字符串只包含了 Latin-1,則 coder 屬性值為 0,反之則為 1。

不可變

查看String類的代碼可以發現,String類被final關鍵字修飾,因此這個類不能被繼承,并且String類里面的變量char 數組也被 final 修飾了,因此String對象不能被修改。

String對象不可變主要有如下幾個優點:

第一,保證 String 對象的安全性。假設 String 對象是可變的,那么 String 對象將可能被惡意修改。

第二,保證 hash 屬性值不會頻繁變更,確保了唯一性,使得類似 HashMap 容器才能實現相應的 key-value 緩存功能。

第三,可以實現字符串常量池。

在 Java 中,通常有兩種創建字符串對象的方式:

第一種是通過字符串常量的方式創建,如String str = "abc"。

第二種是字符串變量通過new 形式的創建,如 String str = new String("abc")。

當代碼中使用第一種方式創建字符串對象時,在編譯類文件時,”abc”常量字符串將會放入到常量結構中,在類加載時,“abc”將會在常量池中創建;然后,str將引用常量池中的字符串對象。這種方式可以減少同一個值的字符串對象的重復創建,節約內存。

String str = new String("abc") 這種方式,首先在編譯類文件時,”abc”常量字符串將會放入到常量結構中,在類加載時,“abc”將會在常量池中創建;其次,在調用new時,JVM 命令將會調用 String 的構造函數,String 對象中的 char 數組將會引用常量池中”abc”字符串的char 數組,在堆內存中創建一個 String 對象;最后,str 將引用 String 對象,String對象的引用跟常量池中”abc”字符串的引用是不一樣的。

對象與引用:對象的內容存儲在內存中,操作系統通過內存地址來找到存儲的內容,引用就是指內存的地址。

比如:String str = new String("abc"),變量str指向的是String對象的存儲地址,也就是說 str 并不是對象,而只是一個對象引用。

字符串拼接

常量相加

String str = "ab" + "cd" + "ef";

查看編譯后的字節碼

0 ldc #2 <abcdef>2 astore_13 return

可以發現編譯器將代碼優化成如下所示

String str= "abcdef";

變量相加

String a = "ab";String b = "cd";String c = a + b;

查看編譯后的字節碼

 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

可以發現,Java在進行字符串相加的時候,底層使用的是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);

輸出結果:

true

在字符串常量中,默認會將對象放入常量池。例如:String a = "123"

在字符串變量中,對象是會創建在堆內存中,同時也會在常量池中創建一個字符串對象,String 對象中的 char 數組將會引用常量池中的 char 數組,并返回堆內存對象引用。例如:String b = new String("abc")

如果調用 intern 方法,會去查看字符串常量池中是否有等于該對象的字符串的引用,如果沒有,在 JDK1.6 版本中會復制堆中的字符串到常量池中,并返回該字符串引用,堆內存中原有的字符串由于沒有引用指向它,將會通過垃圾回收器回收。

在 JDK1.7 版本以后,由于常量池已經合并到了堆中,所以不會再復制具體字符串了,只是會把首次遇到的字符串的引用添加到常量池中;如果有,就返回常量池中的字符串引用。

下面開始分析上面的代碼塊:

在一開始字符串”abc”會在加載類時,在常量池中創建一個字符串對象。

創建 a 變量時,調用 new Sting() 會在堆內存中創建一個 String 對象,String 對象中的 char 數組將會引用常量池中字符串。在調用 intern 方法之后,會去常量池中查找是否有等于該字符串對象的引用,有就返回常量池中的字符串引用。

創建 b 變量時,調用 new Sting() 會在堆內存中創建一個 String 對象,String 對象中的 char 數組將會引用常量池中字符串。在調用 intern 方法之后,會去常量池中查找是否有等于該字符串對象的引用,有就返回常量池中的字符串引用。

而在堆內存中的兩個String對象,由于沒有引用指向它,將會被垃圾回收。所以 a 和 b 引用的是同一個對象。

如果在運行時,創建字符串對象,將會直接在堆內存中創建,不會在常量池中創建。所以動態創建的字符串對象,調用 intern 方法,在 JDK1.6 版本中會去常量池中創建運行時常量以及返回字符串引用,在 JDK1.7 版本之后,會將堆中的字符串常量的引用放入到常量池中,當其它堆中的字符串對象通過 intern 方法獲取字符串對象引用時,則會去常量池中判斷是否有相同值的字符串的引用,此時有,則返回該常量池中字符串引用,跟之前的字符串指向同一地址的字符串對象。

以一張圖來總結 String 字符串的創建分配內存地址情況:

String字符串的示例分析

使用 intern 方法需要注意的一點是,一定要結合實際場景。因為常量池的實現是類似于一個 HashTable 的實現方式,HashTable 存儲的數據越大,遍歷的時間復雜度就會增加。如果數據過大,會增加整個字符串常量池的負擔。

判斷字符串是否相等

// 運行環境 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
// 運行環境 JDK1.8String s1 = new String("1") + new String("1");s1.intern();String s2 = "11";System.out.println(s1 == s2); // true , 如果不執行1.intern(),則返回false

String s1 = new String("1") + new String("1")會在堆中組合一個新的字符串對象"11",在s1.intern()之后,由于常量池中沒有該字符串的引用,所以常量池中生成一個堆中字符串"11"的引用,此時String s2 = "11"返回的是堆字符串"11"的引用,所以s1==s2。

在JDK1.7版本以及之后的版本運行以下代碼,你會發現結果為true,在JDK1.6版本運行的結果卻為false:

String s1 = new String("1") + new String("1");System.out.println( s1.intern()==s1);

StringBuilder與StringBuffer

由于String的值是不可變的,這就導致每次對String的操作都會生成新的String對象,這樣不僅效率低下,而且大量浪費有限的內存空間。

和 String 類不同的是,StringBuffer 和 StringBuilder 類的對象能夠被多次的修改,并且不產生新的對象。

StringBuilder 類在 Java 5 中被提出,它和 StringBuffer 之間的最大不同在于 StringBuilder 的方法不是線程安全的(不能同步訪問)。

由于 StringBuilder 相較于 StringBuffer 有速度優勢,所以多數情況下建議使用 StringBuilder 類。然而在應用程序要求線程安全的情況下,則必須使用 StringBuffer 類。

以上是“String字符串的示例分析”這篇文章的所有內容,感謝各位的閱讀!希望分享的內容對大家有幫助,更多相關知識,歡迎關注億速云行業資訊頻道!

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

亚洲午夜精品一区二区_中文无码日韩欧免_久久香蕉精品视频_欧美主播一区二区三区美女