一:StringBuffer的底層
(1)線程安全的字符串操作類
(2)通過synchronized關鍵字聲明同步方法,保證多線程環境下數據安全
1 @Override 2 public synchronized StringBuffer append(String str) { 3 toStringCache = null; 4 super.append(str); 5 return this; 6 }
(3)底層存儲數據的Char[]數組,初始化時,該數組的長度是16。如果構造函數有新傳入字符轉str,則16基礎上加str.length.
1 /** 2 *無參構造 3 */ 4 public StringBuffer() { 5 super(16); 6 } 7 8 9 10 /** 11 *帶參構造 12 */ 13 public StringBuffer(String str) { 14 super(str.length() + 16); 15 append(str); 16 } 17 18 /** 19 *初始化char[]數組 20 */ 21 AbstractStringBuilder(int capacity) { 22 value = new char[capacity]; 23 }
(4)添加字符串的過程
-->先檢查內部char[]數組是否需要擴容
-->如需要擴容則進行擴容,然后將原來元數據copy到新數組中。
-->再將新添加的元數據加入到新char[]數組中
1 public AbstractStringBuilder append(String str) { 2 if (str == null) 3 return appendNull(); 4 int len = str.length(); 5 //檢查char[]數組是否需要擴容,擴容,並將原來的數據copy進去新擴容的數組中 6 ensureCapacityInternal(count + len); 7 //將新添加的數據添加到StringBuilder中的char[]數組中,實現字符串的添加 8 str.getChars(0, len, value, count); 9 count += len; 10 return this; 11 } 12 13 14 /** 15 *元數組char[]的擴容過程 16 */ 17 void expandCapacity(int minimumCapacity) { 18 int newCapacity = value.length * 2 + 2; 19 if (newCapacity - minimumCapacity < 0) 20 newCapacity = minimumCapacity; 21 if (newCapacity < 0) { 22 if (minimumCapacity < 0) // overflow 23 throw new OutOfMemoryError(); 24 newCapacity = Integer.MAX_VALUE; 25 } 26 value = Arrays.copyOf(value, newCapacity); 27 } 28 29 30 /** 31 *擴容實現 32 */ 33 public static char[] copyOf(char[] original, int newLength) { 34 char[] copy = new char[newLength]; 35 System.arraycopy(original, 0, copy, 0, 36 Math.min(original.length, newLength)); 37 return copy; 38 }
二:StringBuillder的底層
(1)線程非安全的字符串操作類
(2)字符串的添加沒有加同步處理,涉及到數組擴容,容易產生臟數據,破壞數據正確性
(3)底層結構和StringBuffer實現基本一樣,只是沒有做同步處理。
--->StringBuffer和StringBuillder都繼承抽象類AbstractStringBuilder,該抽象類實現了字符串操作的方法。
--->StringBuffer和StringBuillder的實現,運用了模板方法的設計模式,將核心數據操作放在父類方法里,子類實現自己的獨有特色的功能,涉及核心操作,調用父類方法。
三:String的底層
String類沒有提供用於修改字符串的方法。String類對象為不可變字符串,如字符串string=”HELLO”永遠只包含HELLO這幾個字母,而不能修改其中任何一個字符。當然可以修改字符串變量string的引用,讓它引用另一個字符串。
不可變字符串有一個優點:編譯器可以讓字符串實現共享。實際上只有字符串常量(使用“ ”聲明,存儲在字符串常量池中)是共享的,subStrng,+等操作產生的結果不能共享。
比較字符串值是否相等時使用equals()方法,不能使用==,==比較的是字符串的地址是否相同。如果字符串在常量池中,可以使用==比較,因為指向的都是同一個字符串。
直接使用 ” ” 聲明的String對象會直接存儲在常量池中,(可以實現共享)
1.String str1=”first”;
jvm在運行時先查找常量池中是否有該字符串,如果有則直接返回該字符串的引用給first(實現了字符串 的共享) ;否則先在常量
池中創建該字符串並返回引用。
此時只會在常量池中創建String對象,不會在堆中創建。
2.String str2=new String(“second”);
該代碼生成了兩個String對象。因為使用了“”會現在常量池中查找是否存在second對象,沒有則創建
否則不創建;在常量池創建完成后,由於使用了new,jvm會在堆中創建內容相同的String對象,並將引用
返回給str2.
3.String str3=”what”; String str4=str3+”a nice day”;
運行時,+ 相當於new,所以堆中會有“what a nice day”對象;常量池中會有”what” “a nice day”兩個對象,而不會有”what a nice day”對象。
4.三者在執行速度方面的比較:StringBuilder > StringBuffer > String
5.測試類
1 package com.yeepay.sxf.mianshi.pagkage; 2 3 public class StringBufferAndStringBuillder { 4 5 public static void main(String[] args) { 6 // String a="abc"; 7 // String b=new String(a); 8 // //【true】a和b比較的是內容。便利各自的char[]數組進行比較 9 // System.out.println("a和b比較==>"+a.equals(b)); 10 // //【false】 a和b比較的是地址。a在常量池中 b在堆內存中 11 // System.out.println("a和b比較==>"+a==b); 12 13 test02(); 14 15 } 16 17 18 public static void test01(){ 19 /** 20 * 你會很驚訝的發現,生成str對象的速度簡直太快了,而這個時候StringBuffer居然速度上根本一點都不占優勢。其實這是JVM的一個把戲,實際上: 21 String str = “This is only a” + “ simple” + “test”; 22 23 其實就是: 24 String str = “This is only a simple test”; 25 26 所以不需要太多的時間了。但大家這里要注意的是,如果你的字符串是來自另外的String對象的話,速度就沒那么快了,譬如: 27 28 String str2 = “This is only a”; 29 30 String str3 = “ simple”; 31 32 String str4 = “ test”; 33 34 String str1 = str2 +str3 + str4; 35 36 這時候JVM會規規矩矩的按照原來的方式去做。 37 */ 38 } 39 40 41 public static void test02(){ 42 //string3指向常量池中的字符串second 43 //string4指向堆中的字符串second 44 //所以值相同,引用不同 45 String string3="second"; 46 String string4=new String("second"); 47 System.out.println(string3==string4); 48 System.out.println(string3.equals(string4)); 49 50 //string5指向常量池中的字符串third 51 //string6一開始指向堆中的字符串third,但是調用intern()方法之后,且該方法調用時先檢查常量池中是否有值為string6 52 //的字符串,如果有則返回該字符串的引用,否則在常量池中創建該字符串,並返回引用 53 //所以一開始引用不相等,后來相等 54 String string5="third"; 55 String string6=new String("third"); 56 System.out.println(string5==string6); 57 string6=string6.intern(); 58 System.out.println(string5==string6); 59 } 60 }
