Java那點事——StringBuffer與StringBuilder原理與區(qū)別
最近在找工作,考官問我一個簡單的題目:“StringBuffer與StringBuilder的區(qū)別,它們的應(yīng)用場景是什么?”,下面小編答案分享給大家,方便以后大家學習,以此也做個備錄。
其實只要找下Google大神就有答案了:StringBuffer 與 StringBuilder 中的方法和功能完全是等價的,只是StringBuffer 中的方法大都采用了 synchronized 關(guān)鍵字進行修飾,因此是線程安全的,而 StringBuilder 沒有這個修飾,可以被認為是線程不安全的。
為了更好的理解上述的答案,還是直接看StringBuffer與StringBuilder的源碼實現(xiàn)比較實在,作為一個程序猿,“有疑問,看源碼”才是正道,我可以負責任的說,當然了得有條件才行!
jdk的實現(xiàn)中StringBuffer與StringBuilder都繼承自AbstractStringBuilder,對于多線程的安全與非安全看到StringBuffer中方法前面的一堆synchronized就大概了解了。
這里隨便講講AbstractStringBuilder的實現(xiàn)原理:我們知道使用StringBuffer等無非就是為了提高java中字符串連接的效率,因為直接使用+進行字符串連接的話,jvm會創(chuàng)建多個String對象,因此造成一定的開銷。AbstractStringBuilder中采用一個char數(shù)組來保存需要append的字符串,char數(shù)組有一個初始大小,當append的字符串長度超過當前char數(shù)組容量時,則對char數(shù)組進行動態(tài)擴展,也即重新申請一段更大的內(nèi)存空間,然后將當前char數(shù)組拷貝到新的位置,因為重新分配內(nèi)存并拷貝的開銷比較大,所以每次重新申請內(nèi)存空間都是采用申請大于當前需要的內(nèi)存空間的方式,這里是2倍。
接下來,玩些好玩的!
在Google中出來了這么一些信息:
【
StringBuffer 始于 JDK 1.0
StringBuilder 始于 JDK 1.5
從 JDK 1.5 開始,帶有字符串變量的連接操作(+),JVM 內(nèi)部采用的是
StringBuilder 來實現(xiàn)的,而之前這個操作是采用 StringBuffer 實現(xiàn)的。
】
我們通過一個簡單的程序來看其執(zhí)行的流程:
清單1 Buffer.java
public class Buffer {
public static void main(String[] args) {
String s1 = "aaaaa";
String s2 = "bbbbb";
String r = null;
int i = 3694;
r = s1 + i + s2;
for(int j=0;i<10;j++){
r+="23124";
}
}
}
使用命令javap -c Buffer查看其字節(jié)碼實現(xiàn):

清單2 Buffer類字節(jié)碼
將清單1和清單2對應(yīng)起來看,清單2的字節(jié)碼中l(wèi)dc指令即從常量池中加載“aaaaa”字符串到棧頂,istore_1將“aaaaa”存到變量1中,后面的一樣,sipush是將一個短整型常量值(-32768~32767)推送至棧頂,這里是常量“3694”,更多的Java指令集請查看另一篇文章“Java指令集”。
讓我們直接看到13,13~17是new了一個StringBuffer對象并調(diào)用其初始化方法,20~21則是先通過aload_1將變量1壓到棧頂,前面說過變量1放的就是字符串常量“aaaaa”,接著通過指令invokevirtual調(diào)用StringBuffer的append方法將“aaaaa”拼接起來,后續(xù)的24~30同理。最后在33調(diào)用StringBuffer的toString函數(shù)獲得String結(jié)果并通過astore存到變量3中。
看到這里可能有人會說,“既然JVM內(nèi)部采用了StringBuffer來連接字符串了,那么我們自己就不用用StringBuffer,直接用”+“就行了吧!“。是么?當然不是了。俗話說”存在既有它的理由”,讓我們繼續(xù)看后面的循環(huán)對應(yīng)的字節(jié)碼。
37~42都是進入for循環(huán)前的一些準備工作,37,38是將j置為1。44這里通過if_icmpge將j與10進行比較,如果j大于10則直接跳轉(zhuǎn)到73,也即return語句退出函數(shù);否則進入循環(huán),也即47~66的字節(jié)碼。這里我們只需看47到51就知道為什么我們要在代碼中自己使用StringBuffer來處理字符串的連接了,因為每次執(zhí)行“+”操作時jvm都要new一個StringBuffer對象來處理字符串的連接,這在涉及很多的字符串連接操作時開銷會很大。
相關(guān)文章
maven升級版本后報錯:Blocked mirror for repositories
本文主要介紹了maven升級版本后報錯:Blocked mirror for repositories,文中的解決方法非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧2023-09-09
springboot項目部署在linux上運行的兩種方式小結(jié)
這篇文章主要介紹了springboot項目部署在linux上運行的兩種方式小結(jié),具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2021-07-07
SpringBoot使用Redis清除所有緩存實現(xiàn)方式
文章介紹通過遍歷刪除Redis所有key的方法,推薦使用RedisTemplate的delete方法而非StringRedisTemplate,需確保緩存操作統(tǒng)一使用同類型模板,避免類型不匹配問題2025-08-08
SpringCloud通過MDC實現(xiàn)分布式鏈路追蹤
在DDD領(lǐng)域驅(qū)動設(shè)計中,我們使用SpringCloud來去實現(xiàn),但排查錯誤的時候,通常會想到Skywalking,但是引入一個新的服務(wù),增加了系統(tǒng)消耗和管理學習成本,對于大型項目比較適合,但是小的項目顯得太過臃腫了,所以本文介紹了SpringCloud通過MDC實現(xiàn)分布式鏈路追蹤2024-11-11
在IDEA中配置Selenium和WebDriver的具體操作
在自動化測試領(lǐng)域Selenium是一款非常流行的開源工具,它支持多種瀏覽器,并提供了豐富的API供開發(fā)者使用,而WebDriver則是Selenium的一個重要組件,它負責驅(qū)動瀏覽器執(zhí)行測試腳本,這篇文章主要給大家介紹了在IDEA中配置Selenium和WebDriver的具體操作,需要的朋友可以參考下2024-10-10

