本文介紹了String類對字符串進行拼接的方法 和拼接字符串的效率分析 以及能對字符串內(nèi)容進行修改的StringBuilder和StringBuffer類其常用方法和區(qū)別 , 最后介紹了兩個字符串經(jīng)典面試題
一.String類概括
1.String常用方法學習->2.String的不可變和字符串常量池->StringBuilder和StringBuffer
String類是Java中用來描述操作字符串的類
上兩篇博客介紹了介紹了常用的方法,以及String的特性:String內(nèi)容是不可變的以及字符串常量池,感興趣的可以去看看…
二.StringBuilder和StringBuffer
StringBulder和StringBuffer是和String一樣都是對字符串進行操作的類
而學了String后知道,String內(nèi)容是不可變的,對String的內(nèi)容進行修改都會創(chuàng)建一個新的字符串返回
而創(chuàng)建新的String對象比原字符串修改所耗費的時間空間更多,效率低很多
因此,java中提供的StringBuilder和StringBuffer是專門用來對字符串進行原地修改的類,并且它們都可以和String類之間進行相互轉(zhuǎn)換
通過源碼看到 這兩個類的方法大致都相同,且都extends繼承了AbstractStringBuilder類
而AbstractStringBuilder類 底層是存在一個value 數(shù)組引用和count變量的,其沒有被private和final修飾 能夠在StringBuilder和StringBuffer中訪問到
value數(shù)組即維護的是字符串內(nèi)容 , 而count記錄的是value內(nèi)有效的字符個數(shù)即真正的字符串長度
(因為value指向的字符數(shù)組長度是當前存放字符串的空間,還有預留的空間用來新增字符等, count記錄的才是字符串真正的長度)
由于String的不可更改特性,為了方便字符串的修改,Java中又提供StringBuilder和StringBuffer類。這兩個類大部分功能是相同的
1.字符串拼接
在String方法中提供concat和join方法 可以使字符串之間進行拼接
concat是成員方法,使一個字符串對象內(nèi)容與另一個字符串對象進行連接成一個新的字符串對象返回
join是靜態(tài)成員方法, 方法第一個參數(shù) 是連接的分隔字符串, 后續(xù)2~n個是用來連接的字符串, 會將其拼接在一起 用一個參數(shù)做分隔(分隔連接的每段字符串,如果只有一段則不顯示分隔符),最后返回此拼接的字符串對象
但上面這些方法并不具備將其他數(shù)據(jù)類型的數(shù)據(jù)拼接進字符串的功能因此使用這些方法還要拼接其他非String類型的數(shù)據(jù)還要將其先轉(zhuǎn)換為字符串再進行拼接
而在Java中,String類型 用 + 連接 String字符串數(shù)據(jù)和任意類型數(shù)據(jù) 都可以使其簡單快捷直接拼接為一個新字符串對象
示例:
public static void main(String[] args) {
System.out.println("12"+3);
System.out.println(3+"12");
System.out.println(1+2+"12");
System.out.println("12"+1+2);
System.out.println(false+"12"+true);
//代碼輸出結(jié)果是什么?
}
字符串類型數(shù)據(jù)和任何數(shù)據(jù)類型數(shù)據(jù) 之間使用+運算,都可以拼接成一個新的字符串對象,
但注意要運算符的優(yōu)先級, 1+2+“12” 是先計算1+2 結(jié)果為3 再3+“12"拼接成字符串"312”
“12”+1+2,是先"12"+1拼接成字符串"121" 再+2 拼接成字符串"1212"
boolean類型數(shù)據(jù)可以參與字符串拼接
“”+12 可以使 12數(shù)字拼接為字符串"12"
通過+號可以快速實現(xiàn)字符串拼接,這種用法很方便讓我們使基本數(shù)據(jù)類型快速變?yōu)樽址愋?/p>
StringBuilder和StringBuffer也可以實現(xiàn)字符串拼接, 需要調(diào)用其內(nèi)部的append方法:
public static void main(String[] args) {
StringBuilder stringBuilder=new StringBuilder();
stringBuilder.append(1+2);
stringBuilder.append(12);
System.out.println(stringBuilder);
stringBuilder.append(3);
System.out.println(stringBuilder);
stringBuilder.append(false);
System.out.println(stringBuilder);
}
可以看到通過StringBuilder對象 使用其append方法可以將任意數(shù)據(jù)類型拼接成一個字符串
而append方法內(nèi)部最后返回的都是StringBuilder對象本身,并沒有創(chuàng)建新對象…
從運行效率上String類型使用+和其他數(shù)據(jù)拼接 和StringBuiler以及的append方法哪種更好?
此時可以使用Java內(nèi)一種計算內(nèi)部運行時間的方法System.currentTimeMillis()采取差值運算, 來分別測試這三個的運行時間…
示例:
public static void main(String[] args) {
long start = System.currentTimeMillis(); // 計算當前內(nèi)部運行時間
String s = "";
for(int i = 0; i < 10000; ++i){
s += i; //字符串類型 加任意基本數(shù)據(jù)類型 都會拼接成一個新字符串再給s引用接收
} // 每+=一次 會額外創(chuàng)建三個對象
long end = System.currentTimeMillis();
System.out.println(end - start);
start = System.currentTimeMillis();
StringBuffer sbf = new StringBuffer(""); // 需要加鎖解鎖
for(int i = 0; i < 10000; ++i){
sbf.append(i); //對字符串內(nèi)的數(shù)組進行操作 不會創(chuàng)建額外字符串
}
end = System.currentTimeMillis();
System.out.println(end - start);
start = System.currentTimeMillis();
StringBuilder sbd = new StringBuilder();
for(int i = 0; i < 10000; ++i){
sbd.append(i); //對字符串內(nèi)的數(shù)組進行操作 不會創(chuàng)建額外字符串
}
end = System.currentTimeMillis();
System.out.println(end - start);
}
通過運行結(jié)果可大概估計:拼接效率:String<StringBuffer<StringBuilder
為什么String類型使用+進行字符串拼接效率比StringBuilder和StringBuffer慢很多?
通過 javap -c 字節(jié)碼文件 反匯編此文件, 能看到使用+號底層做了些什么事
使用+進行拼接, 本質(zhì)上是先new了一個StringBuilder對象,然后調(diào)用append方法將s字符串追加進去,再調(diào)用append 將i 追加進去, 最后調(diào)用了toString方法返回~
而StringBuilder重寫了toString方法,其作用是 實例一個新String對象,
將StringBuilder繼承的AbstractStringBuilder的value數(shù)組引用指向的數(shù)組 和 ,0 ,count作為參數(shù)傳參調(diào)用其構(gòu)造方法,
作用是 創(chuàng)建一個String對象 其內(nèi)容是 value指向的數(shù)組對象 0~count之間的內(nèi)容 (注意:String內(nèi)的value是指向的新value數(shù)組)
可見:使用+號拼接字符串,在底層是使用了StringBuilder對象的append方法,最后返回的也是一個新String對象…
那在上述代碼中, 使用for循環(huán)通過+號拼接了10000個i, 每次拼接都會創(chuàng)建一個StringBuilder對象和String對象, 可見完成此操作需要new如此多的對象,而且還要回收掉未被指向的String對象, 消耗非常大
而直接實例StringBuilder對象通過append追加字符串內(nèi)容, 全部追加完成后最后只需要將StringBuilder對象轉(zhuǎn)換String對象 進行一次操作即可, 在此操作里只額外實例化了兩個對象, 開銷比+號少的多
故:在使用大量字符串追加操作時, 不建議使用+號操作符進行拼接, 其性能遠遠低于用StringBuilder進行append拼接, 而使用StringBuilder效率比其他拼接方法效率更高
(“1”+“2” 字符串常量用+號拼接不會進行上述append在底層直接看成"12"字符串)
2.StringBuilder和StringBuffer常用方法和區(qū)別
StringBuilder和StringBuffer都能對字符串進行修改, 其內(nèi)部大部分方法也是相同
以下是一些常用的其內(nèi)能對字符串內(nèi)容進行修改的方法如:
方法 | 作用 |
---|---|
append() | 在尾部追加,相當于String的+=,可以追加:boolean、char、char[]、double、float、int、long、Object、String、StringBuff的變量 |
void setCharAt(int index,char ch) | 將index位置的字符設(shè)置為ch |
insert(int offset, String str) | 在offset位置插入:八種基類類型 & String類型 & Object類型數(shù)據(jù) |
deleteCharAt(int index) | 刪除index位置字符 |
delete(intstart, int end) | 刪除[start, end)區(qū)間內(nèi)的字符 |
replace(intstart, int end, String str) | 將[start, end)位置的字符替換為str |
reverse() | 將字符串進原地逆置 |
toString() | 將內(nèi)容以String對象形式返回 |
以上是StringBulider和StringBuffer常用到的對字符串內(nèi)容進行修改的方法,其內(nèi)還有一些和Stirng類一樣的方法如 indexOf(),具體可自己去了解
既然大部分方法都是相同的,那這兩個類的具體區(qū)別在哪?
因為String類不能對字符串進行修改,其設(shè)計可以使線程更安全,每個線程對方法的調(diào)用都是創(chuàng)建新字符串對象, 而 StringBuilder能對字符串內(nèi)容進行修改,那就會導致多個線程同時調(diào)用一個方法會產(chǎn)生一些問題… 此時正是要解決這個線程不安全的問題
通過源碼可以看到,StringBuffer內(nèi)大部分的方法都有synchronized(同步) 關(guān)鍵字修飾 ,被此關(guān)鍵字修飾的方法即為同步方法, 其是線程安全的, 表示在多線程情況下, 不同線程同時調(diào)用這個方法時會將其上鎖,同一時間只能有一個線程執(zhí)行此方法,其它線程只能等待執(zhí)行完這個方法解鎖后才能一一執(zhí)行此方法
而在上面StringBuffer 和StringBuilder 對象 對10000個i進行拼接時, StringBuilder比其效率高一點,也是因為 StringBuffer 執(zhí)行方法時 還要 進行加鎖解鎖操作,此也是要消耗資源的…
故StringBuffer類用在多線程情況下,對字符串對象進行修改 而StringBuilder用在單線程情況下
3. 面試題:以下代碼共創(chuàng)建多少個對象?
public static void main(String[] args) {
String str=new String("123"); // 創(chuàng)建多少個對象
String str1=new String("1"+"2")+new String("3"); //創(chuàng)建多少個對象
//以上程序在運行時字符串常量都是第一次出現(xiàn)字符串常量池并不存在程序中的常量字符串
}
在程序運行時,并 str代碼 一個"123"對象并存放在字符串常量池里,new String()一個對象; 最后 創(chuàng)建了兩個String對象
str1處 首先new了一個StringBuilder對象 再 new String() 一個對象 , “1”+“2” 底層直接編程成"12" 是一個String對象 , 再被拼接到StringBuilder中, 然后在new String對象, 然后"3"一個對象 , 再被拼接到StringBuilder對象中 , 最后 StringBuilder調(diào)用了toString又new了一個String對象 返回 , 最后 new了1個StringBuilder對象和五個String對象
注意str1最后接受的字符串內(nèi)容是123 但是其和字符串常量池中的"123" 是不同的
字符串常量池是在編譯時將其字符串常量"123"存放進去的, 而str1接受的是后續(xù)拼接出來的字符串123
4.面試題: String 、StringBuilder和StringBuffer的異同
三者都是用來描述操作字符串的類
String類的內(nèi)容是不可變的所有對String內(nèi)容進行修改的操作都是創(chuàng)建新String對象
,StringBuilder和StringBuffer的內(nèi)容都是可變的,常用來對字符串內(nèi)容進行頻繁修改時使用,
StringBuffer與StringBuilder大部分功能是相似的
StringBuffer采用同步處理,屬于線程安全操作;而StringBuilder未采用同步處理,屬于線程不安全操作
三.總結(jié)
本文介紹了字符串拼接的方法 (String的成員方法concat和類成員方法join +號運算符拼接 StringBuilder和StringBuffer的append拼接 )以及各種拼接方法的效率 和底層反匯編分析, StringBuffer和StringBuilder的大部分對內(nèi)容進行修改的方法,和其不同點, 以及兩大字符串方面的面試題 及其分析文章來源:http://www.zghlxwxcb.cn/news/detail-448515.html
文章來源地址http://www.zghlxwxcb.cn/news/detail-448515.html
到了這里,關(guān)于String類的學習筆記(下):字符串拼接以及StringBuilder和StringBuffer的學習的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!