1、引言
在上一篇文章中,我們理解了字符串的常用方法,細心的同學大概已經發現,不管是將字符串中的字符轉變為大寫或小寫,或是完成字符串的替換,又或是去除空白字符等等,只要涉及到字符串的修改,我們都是生成了一個新的字符串,而不是改變原有的字符串。
例如(toLowerCase方法的源碼,返回新的字符串對象):
這是因為String類的對象是不可以被修改的(字符串具有不可變性)。關于字符串為什么不可以被修改,答案就在下面的文章中~
2、字符串為什么具有不可變性
2.1 String類在源碼中的設計
2.1.1 String類被final所修飾
為什么不可被修改呢?我們先來觀察String類的源碼:
我們可以看到String類被final所修飾,但是這并不是字符串不可變的原因,因為當類被final修飾時,只能說明這個類不能被繼承,也就是說String類不可被繼承,而不是不可變。
2.1.2 value被final所修飾
我們繼續往下看,
我們又發現,value數組也被final修飾了,我們知道,String類的value數組才真正存儲了字符串的內容,到這里,有的同學就開始激動了,就說:"value數組被final修飾了,變成了常量,常量不可變,所以字符串就具有不可變性!!!"。
但事實并不是這樣,因為數組是一個引用類型,當引用類型被final修飾,只能說明當前引用變量的指向不能改變,而并不是不能修改它所指向的內容,我們來觀察以下代碼:
我們發現,當數組被final修飾后,我們可以改變它的內容,但是不能改變它的指向,也就是說final修飾value數組并不是字符串不可變的原因。(很多同學都會在這里產生誤解)
2.1.3 value數組被private修飾封裝
其實這點才是字符串不可變的真正原因,value數組被封裝在了String類當中,沒有提供任何的get和set方法,無法獲取到字符串,當然也不可能被修改。
2.2?總結
1.當一個類被final修飾,說明這個類不能被繼承。也就是說,String類被final修飾不是字符串不可變的原因。
2.當一個引用類型被final所修飾,說明這個引用的指向不能改變,但是可以修改這個引用所指向的內容。也就是說,value被final修飾不是字符串不可變的原因。
3.value被private修飾,被封裝起來才是字符串不可變的真正原因。
3、字符串的修改
3.1 使用"+"對字符串拼接
我們已經理解了字符串為什么不可變,也知道了字符串的修改實質上會再創建一個新對象,
因此,我們每使用一次"+"來完成字符串的拼接(以及對字符串進行修改)實質上就是創建了一個新的String類對象:
而每次都要創建新對象,會占用大量的內存空間,效率非常低下,所以我們不推薦這樣來完成字符串的拼接,為了提高效率,我們使用StringBuilder和StringBuffer類來完成對字符串的修改。
3.2 StringBuilder和StringBuffer類
StringBuilder和StringBuffer的功能大部分是相同,我們這里就以StringBuilder來講。
3.2.1?append方法完成拼接
我們可以通過append來完成字符串的拼接(拼接在尾部,相當于String的"+"):
我們可以通過append來拼接多種類型,且返回值都是this(說明在原來的串上進行的修改,不會產生新的對象):
拼接完成后,我們可以調用StringBuilder中重寫的toString方法,使用String類型來接收生成的字符串:
這樣,可以減少額外對象的生成,大大的提高了效率!
3.2.2?StringBuilder和StringBuffer中的字符串修改方法
我們發現,在這兩大類中,提供了能夠修改字符串的方法,很方便的供我們使用。
例如reverse方法:
我們可以很輕松的在原來的字符串上完成字符的逆置。
例如insert方法:
我們可以在指定下標處完成插入。
注:這些方法都是在原本的字符串上進行的修改,不會生成新的對象,效率很高。
4、String、StringBuilder、StringBuffer的區別
1.String的內容不可被修改,StringBuilder和StringBuffer的內容可以被修改。
2.StringBuilder和StringBuffer的功能大體相同。
3.StringBuffer采用同步處理,屬于線程安全操作;而StringBuilder未采用同步處理,屬于線程不安全操作。(學習中,后面再說~)