Java
基礎之字符串操做——String
segmentfault
什麼是字符串?若是直接按照字面意思來理解就是多個字符鏈接起來組合成的字符序列。爲了更好的理解以上的理論,咱們先來解釋下字符序列,字符序列:把多個字符按照必定的順序排列起來;而字符序列就是做爲字符串的內容而存在的。因此能夠把字符串理解爲:把多個字符按照必定的順序排列起來而構成的排列組合。數組
若是仍是很差理解,沒有關係,我還有法寶。咱們能夠用烤串來比喻說明,能夠把字符串看做是烤串,烤串上的每一塊肉都至關因而一個字符。把一塊塊肉按照肥瘦相間的順序排列並串起來便成了咱們吃的烤串,同理,把多個字符按照必定的順序「串」起來就構成了字符串。安全
字符串的分類,字符串分爲可變的字符串和不可變的字符串兩種;這裏的不可變與可變指的是字符串的對象仍是不是同一個,會不會由於字符串對象內容的改變而建立新的對象。多線程
Java
中的String
類的對象就是不可變的。StringBuilder
類和StringBuffer
類的對象就是可變的;當對象建立完畢以後,該對象的內容發生改變時不會建立新的對象,也就是說對象的內容能夠發生改變,當對象的內容發生改變時,對象保持不變,仍是同一個。String類表示不可變的字符串,當前String
類對象建立完畢以後,該對象的內容(字符序列)是不變的,由於內容一旦改變就會建立一個一個新的對象。併發
String對象的建立:app
String s1 = "laofu";
須要注意這裏是雙引號:""
,區別與字符char類型的單引號:''
;String s2 = new String("laofu");
;以上兩種建立方式的對象在JVM中又是如何分佈的呢? 分別有什麼區別呢?性能
方式一和方式二在JVM中又是如何分佈?測試
上圖中的常量池:用於存儲常量的地方內存區域,位於方法區中。常量池又分爲編譯常量池和運行常量池兩種:優化
JVM
的時候,其中存儲的是字節碼的相關信息(如:當前執行到的字節碼行號等)。方式一和方式二有何不一樣?ui
String s1 = "laofu";
有可能只建立一個String
對象,也有可能建立不建立String
對象;若是在常量池中已經存在"laofu",那麼對象s1
會直接引用,不會建立新的String
對象;不然,會先在常量池先建立常量"laofu"
的內存空間,而後再引用。String s2 = new String("laofu");
最多會建立兩個String
對象,最少建立一個String
對象。可以使用new
關鍵字建立對象是會在堆空間建立內存區域,這是第一個對象;而後對象中的字符串字面量可能會建立第二個對象,而第二個對象如方式一中所描述的那樣,是有可能會不被建立的,因此至少建立一個String
對象。字符串的本質,字符串在底層其實就是char[]
,char
表示一個字符,好比:
String str = "laofu"; 等價於 char[] cs = new char[]{'l','a','o','f','u'};
String對象的空值:
String s1 = null;
此時s1
沒有初始化,也在JVM
中沒有分配內存空間。String s2 = "";
此時對象s2已經初始化,值爲""
,JVM
已經爲其分配內存空間。字符串的比較:使用"=="
和"equals"
會有不一樣效果,詳情在以前的文章中分享過:[JAVA] 只知對象屬性,不知類屬性?就算類答應,static都不答應
==
號:用於比較對象引用的內存地址是否相同。equals
方法:在Object
類中和==
號相同,但在自定義類中,建議覆蓋equals
方法去實現比較本身內容的細節;因爲String
類覆蓋已經覆蓋了equals
方法,因此其比較的是字符內容。
因此能夠這樣來判斷字符串非空:
s1 != null;
;""
):"".equals(s1);
;若是上述兩個條件都知足,說明字符串確實爲空!
字符串拼接:Java
中的字符串能夠經過是"+"
實現拼接,那麼代碼中字符串拼接在JVM
中又是如何處理的呢?咱們經過一個例子說明:經過比較拼接字符串代碼編譯先後的代碼來查看JVM
對字符串拼接的處理。
經過上述例子不難發現,JVM
會對字符串拼接作一些優化操做,若是字符串字面量之間的拼接,不管有多少個字符串,JVM
都會同樣的處理;若是是對象之間拼接,或者是對象和字面量之間的拼接,亦或是方法執行結果參與拼接,String
內部會使用StringBuilder
先來獲取對象的值,而後使用append
方法來執行拼接。由此能夠總結得出:
""
引號建立的字符串都是直接量,在編譯期就會將其存儲到常量池中;new String("")
建立的對象會存儲到堆內存中,在運行期才建立;"aa" + "bb"
建立的也是直接量,這樣的字符串在編譯期就能肯定,因此也會存儲到常量池中;String
直接量的字符串表達式(如"aa" + s1
)建立的對象是運行期才建立的,對象存儲在堆中,由於其底層是創新了StringBuilder
對象來實現拼接的;5. 不管是使用變量,仍是調用方法來鏈接字符串,都只能在運行期才能肯定變量的值和方法的返回值,不存在編譯優化操做。
這裏列舉了一些經常使用String API
,更多的能夠查閱jdk使用手冊
,作Java
必定得學會查閱jdk手冊
。
String 的建立和轉換:
byte[] getBytes()
:把字符串轉換爲byte
數組。char[] toCharArray()
:把字符串轉換爲char
數組。String(byte[] bytes)
:把byte
數組轉換爲字符串。String(char[] value)
:把char
數組轉換爲字符串。獲取字符串信息
int length()
:返回此字符串的長度。char charAt(int index)
:返回指定索引處的 char
值。int indexOf(String str)
:返回指定字符串在此字符串中首次(從最左邊算起)出現處的索引。int lastIndexOf(String str)
:返回指定字符串在此字符串中最後(最右邊算起)出現處的索引。字符串比較判斷
boolean equals(Object anObject)
: 將此字符串與指定的對象比較。boolean equalsIgnoreCase(String anotherString)
: 將此 String 與另外一個 String 作忽略大小寫的比較。boolean contentEquals(CharSequence cs)
: 將此字符串與指定的 CharSequence
比較,比較的是內容;
字符串大小寫轉換:調用方法的字符串就是當前字符串
String toUpperCase()
: 把當前字符串轉換爲大寫String toLowerCase()
: 把當前字符串轉換爲小寫先來分別使用String/StringBuilder/StringBuffer
來拼接30000次
字符串,對比各自損耗的時間,通過測試發現:
String
作字符串拼接的時候,耗時最高,性能極低,緣由是String
內容是不可變的,每次內容改變都會在內存中建立新的對象。
性能最好的是StringBuilder
,其次是StringBuffer
,最後是String
。StringBuilder
和StringBuffer
區別並非很大,也有多是測試次數還不夠吧。感興趣的小夥伴能夠增長拼接次數來看看。代碼很簡單,就不展現出來了。
因此在開發中拼接字符串時,優先使用StringBuffer/StringBuilder
,不到萬不得已,不要輕易使用String
。
StringBuilder以及StringBuffer的區別
StringBuffer
和StringBuilder
都表示可變的字符串,兩種’的功能方法都是相同的。但惟一的區別:
StringBuffer
:StringBuffer
中的方法都使用了synchronized
修飾符,表示同步操做,在多線程併發的時候能夠保證線程安全,但在保證線程安全的時候,對其性能有必定影響,會下降其性能。StringBuilder
:StringBuilder
中的方法都沒有使用了synchronized
修飾符,線程不安全,正由於如此,其性能較高。對併發安全沒有很高要求的狀況下,建議使用StringBuilder
,由於其性能很高。像這樣的狀況會較多些。使用StringBuilder
無參數的構造器,在底層建立了一個長度爲16
的char
數組:
此時該數組只能存儲16
個字符,若是超過了16
個字符,會自動擴容(建立長度更大的數組,再把以前的數組拷貝到新數組),此時性能極低;若是事先知道大概須要存儲多少字符,能夠經過構造器來設置字符的初始值:
//建立一個長度爲80的char數組. new StringBuilder(80);
StringBuilder的經常使用方法:
append(Object val)
:追加任意類型數據到當前StringBuilder
對象中。StringBuilder deleteCharAt(int index)
:刪除字符串中指定位置的字符。完結。雖然老夫不正經,但老夫一身的才華