JDK源碼分析之String、StringBuilder和StringBuffer
前言
本文主要介紹了關(guān)于JDK源碼分析之String、StringBuilder和StringBuffer的相關(guān)內(nèi)容,分享出來(lái)供大家參考學(xué)習(xí),下面話不多說了,來(lái)一起看看詳細(xì)的介紹吧
String類的申明
public final class String implements java.io.Serializable, Comparable<String>, CharSequence {…}
String類用了final修飾符,表示它不可以被繼承,同時(shí)還實(shí)現(xiàn)了三個(gè)接口, 實(shí)現(xiàn)Serializable接口表示String類可被序列化;實(shí)現(xiàn)Comparable<T> 接口主要是提供一個(gè)compareTo 方法用于比較String字符串;還實(shí)現(xiàn)了CharSequence 接口,這個(gè)接口代表的是char值得一個(gè)可讀序列(CharBuffer, Segment, String, StringBuffer, StringBuilder也都實(shí)現(xiàn)了CharSequence接口)
String主要字段、屬性說明
/*字符數(shù)組value,存儲(chǔ)String中實(shí)際字符 */ private final char value[]; /*字符串的哈希值 默認(rèn)值0*/ private int hash; /*字符串的哈希值 默認(rèn)值0*/ /*一個(gè)比較器,用來(lái)排序String對(duì)象, compareToIgnoreCase方法中有使用 */ public static final Comparator<String> CASE_INSENSITIVE_ORDER = new CaseInsensitiveComparator();
String 部分方法分析
String類提供了系列的構(gòu)造函數(shù),其中有幾個(gè)都已經(jīng)不推薦使用了,如下圖:
構(gòu)造函數(shù)
以下是兩個(gè)常用的構(gòu)造函數(shù)的實(shí)現(xiàn):
//String str = new String(“123”) public String(String original) { this.value = original.value; this.hash = original.hash; } //String str3 = new String(new char[] {'1','2','3'}); public String(char value[]) { //將字符數(shù)組值copy至value this.value = Arrays.copyOf(value, value.length); }
boolean equals(Object anObject)
String 類重寫了 equals 方法,將此字符串與指定的對(duì)象比較。當(dāng)且僅當(dāng)該參數(shù)不為 null,并且是與此對(duì)象表示相同字符序列的 String 對(duì)象時(shí),結(jié)果才為 true。
public boolean equals(Object anObject) { //直接將對(duì)象引用相比較,相同返回true if (this == anObject) { return true; } //比較當(dāng)前對(duì)象與anObject的字符序列value if (anObject instanceof String) { String anotherString = (String)anObject; int n = value.length; if (n == anotherString.value.length) { char v1[] = value; char v2[] = anotherString.value; int i = 0; while (n-- != 0) { if (v1[i] != v2[i]) return false; i++; } return true; } } return false; }
int compareTo(String anotherString)
逐位比較兩個(gè)字符串的字符序列,如果某一位字符不相同,則返回該位的兩個(gè)字符的Unicode 值的差,所有位都相同,則計(jì)算兩個(gè)字符串長(zhǎng)度之差,兩個(gè)字符串相同則返回0
public int compareTo(String anotherString) { int len1 = value.length; int len2 = anotherString.value.length; //取長(zhǎng)度較小的字符串的長(zhǎng)度 int lim = Math.min(len1, len2); char v1[] = value; char v2[] = anotherString.value; int k = 0; while (k < lim) { //將兩個(gè)字符串的字符序列value逐個(gè)比較,如果不等,則返回該位置兩個(gè)字符的Unicode 之差 char c1 = v1[k]; char c2 = v2[k]; if (c1 != c2) { return c1 - c2; //返回Unicode 之差 } k++; } //長(zhǎng)度較小的字符串所有位都比較完,則返回兩個(gè)字符串長(zhǎng)度之差 //如果兩個(gè)字符串相同,那么長(zhǎng)度之差為0,即相同字符串返回0 return len1 - len2; }
compareToIgnoreCase(String str)方法實(shí)現(xiàn)于此類似,比較時(shí)忽略字符的大小寫,實(shí)現(xiàn)方式如下:
public int compare(String s1, String s2) { int n1 = s1.length(); int n2 = s2.length(); int min = Math.min(n1, n2); for (int i = 0; i < min; i++) { char c1 = s1.charAt(i); char c2 = s2.charAt(i); if (c1 != c2) { c1 = Character.toUpperCase(c1); c2 = Character.toUpperCase(c2); if (c1 != c2) { c1 = Character.toLowerCase(c1); c2 = Character.toLowerCase(c2); if (c1 != c2) { // No overflow because of numeric promotion return c1 - c2; } } } } return n1 - n2; }
native String intern()
當(dāng)調(diào)用 intern 方法時(shí),如果池已經(jīng)包含一個(gè)等于此 String 對(duì)象的字符串(用 equals(Object) 方法確定),則返回池中的字符串。否則,將此 String 對(duì)象添加到池中,并返回此 String 對(duì)象的引用。
所有字面值字符串和字符串賦值常量表達(dá)式都使用 intern 方法進(jìn)行操作,例如:String str1 = "123";
String內(nèi)存位置:常量池OR堆
String對(duì)象可以直接通過字面量創(chuàng)建,也可以通過構(gòu)造函數(shù)創(chuàng)建,有什么區(qū)別呢?
1.通過字面量或者字面量字符串通過”+”拼接的方式創(chuàng)建的String對(duì)象存儲(chǔ)在常量池中,實(shí)際創(chuàng)建時(shí)如果常量池中存在,則直接返回引用,如果不存在則創(chuàng)建該字符串對(duì)象
2.使用構(gòu)造函數(shù)創(chuàng)建字符串對(duì)象,則直接在堆中創(chuàng)建一個(gè)String對(duì)象
3.調(diào)用intern方法,返回則會(huì)將該對(duì)象放入常量池(不存在則放入常量池,存在則返回引用)
下面舉例說明String對(duì)象內(nèi)存分配情況:
String str1 = new String("123"); String str2 = "123"; String str3 = "123"; String str4 = str1.intern(); System.out.println(str1==str2); // false str1在堆中創(chuàng)建對(duì)象,str2在常量池中創(chuàng)建對(duì)象 System.out.println(str2==str3); // true str2在常量池中創(chuàng)建對(duì)象,str3直接返回的str2創(chuàng)建的對(duì)象的引用 所以str2和str3指向常量池中同一個(gè)對(duì)象 System.out.println(str4==str3); // true str4返回常量池中值為"123"的對(duì)象,因此str4和str2、str3都相等
關(guān)于字符串拼接示例:
public class StringTest { public static final String X = "ABC"; // 常量X @Test public void Test() { String str5 = new String("ABC"); String str6 = str5+"DEF"; //堆中創(chuàng)建 String str7 = "ABC"+"DEF"; //常量池 String str8 = X+"DEF"; //X為常量,值是固定的,因此X+"DEF"值已經(jīng)定下來(lái)為ABCDEF,實(shí)際上編譯后得代碼相當(dāng)于String str8 = "ABCDEF" String str9 = "ABC"; String str10 = str9+"DEF"; //堆中 System.out.println(str6==str7); //false System.out.println(str8==str7); //true System.out.println(str10==str7); //false System.out.println(X==str9); //true } }
反編譯后的代碼看一下便一目了然:
內(nèi)存分配如下:
String、StringBuffer、StringBuilder
由于String類型內(nèi)部維護(hù)的用于存儲(chǔ)字符串的屬性value[]字符數(shù)組是用final來(lái)修飾的:
/** The value is used for character storage. */ private final char value[];
表明在賦值后可以再修改,因此我們認(rèn)為String對(duì)象一經(jīng)創(chuàng)建后不可變,在開發(fā)過程中如果碰到頻繁的拼接字符串操作,如果使用String提供的contact或者直接使用”+”拼接字符串會(huì)頻繁的生成新的字符串,這樣使用顯得低效。Java提供了另外兩個(gè)類:StringBuffer和StringBuilder,用于解決這個(gè)問題:
看一下下面的代碼:
String str1="123"; String str2="456"; String str3="789"; String str4 = "123" + "456" + "789"; //常量相加,編譯器自動(dòng)識(shí)別 String str4=“123456789” String str5 = str1 + str2 + str3; //字符串變量拼接,推薦使用StringBuilder StringBuilder sb = new StringBuilder(); sb.append(str1); sb.append(str2); sb.append(str3);
下面是StringBuilder類的實(shí)現(xiàn),只截取了分析的部分代碼:
public final class StringBuilder extends AbstractStringBuilder implements java.io.Serializable, CharSequence { //拼接字符串 @Override public StringBuilder append(String str) { //調(diào)用父類AbstractStringBuilder.append super.append(str); return this; } }
abstract class AbstractStringBuilder implements Appendable, CharSequence { /** * 存儲(chǔ)字符串的字符數(shù)組,非final類型,區(qū)別于String類 */ char[] value; /** * The count is the number of characters used. */ int count; public AbstractStringBuilder append(String str) { if (str == null) return appendNull(); int len = str.length(); //檢查是否需要擴(kuò)容 ensureCapacityInternal(count + len); //字符串str拷貝至value str.getChars(0, len, value, count); count += len; return this; } private void ensureCapacityInternal(int minimumCapacity) { // overflow-conscious code // minimumCapacity=count+str.length //拼接上str后的容量 如果 大于value容量,則擴(kuò)容 if (minimumCapacity - value.length > 0) { //擴(kuò)容,并將當(dāng)前value值拷貝至擴(kuò)容后的字符數(shù)組,返回新數(shù)組引用 value = Arrays.copyOf(value, newCapacity(minimumCapacity)); } } //StringBuilder擴(kuò)容 private int newCapacity(int minCapacity) { // overflow-conscious code // 計(jì)算擴(kuò)容容量 // 默認(rèn)擴(kuò)容后的數(shù)組長(zhǎng)度是按原數(shù)(value[])組長(zhǎng)度的2倍再加上2的規(guī)則來(lái)擴(kuò)展,為什么加2? int newCapacity = (value.length << 1) + 2; if (newCapacity - minCapacity < 0) { newCapacity = minCapacity; } return (newCapacity <= 0 || MAX_ARRAY_SIZE - newCapacity < 0) ? hugeCapacity(minCapacity) : newCapacity; } }
StringBuffer和StringBuilder用一樣,內(nèi)部維護(hù)的value[]字符數(shù)組都是可變的,區(qū)別只是StringBuffer是線程安全的,它對(duì)所有方法都做了同步,StringBuilder是線程非安全的,因此在多線程操作共享字符串變量的情況下字符串拼接處理首選用StringBuffer, 否則可以使用StringBuilder,畢竟線程同步也會(huì)帶來(lái)一定的消耗。
總結(jié)
以上就是這篇文章的全部?jī)?nèi)容了,希望本文的內(nèi)容對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,如果有疑問大家可以留言交流,謝謝大家對(duì)腳本之家的支持。
相關(guān)文章
springMVC?@RestControllerAdvice注解使用方式
這篇文章主要介紹了springMVC?@RestControllerAdvice注解使用方式,下面通過一個(gè)簡(jiǎn)單的示例,演示如何使用?@RestControllerAdvice,感興趣的朋友跟隨小編一起看看吧2024-08-08使用Sharding-JDBC對(duì)數(shù)據(jù)進(jìn)行分片處理詳解
這篇文章主要介紹了使用Sharding-JDBC對(duì)數(shù)據(jù)進(jìn)行分片處理詳解,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-10-10Java 實(shí)現(xiàn)漢字轉(zhuǎn)換為拼音的實(shí)例
這篇文章主要介紹了Java 實(shí)現(xiàn)漢字轉(zhuǎn)換為拼音的實(shí)例的相關(guān)資料,需要的朋友可以參考下2016-12-12在Window系統(tǒng)下安裝Netbeans9的方法
今天小編就為大家分享一篇關(guān)于在Window系統(tǒng)下安裝Netbeans9的方法,小編覺得內(nèi)容挺不錯(cuò)的,現(xiàn)在分享給大家,具有很好的參考價(jià)值,需要的朋友一起跟隨小編來(lái)看看吧2018-12-12