2025年高頻string類面試題及答案_第1頁
2025年高頻string類面試題及答案_第2頁
2025年高頻string類面試題及答案_第3頁
2025年高頻string類面試題及答案_第4頁
2025年高頻string類面試題及答案_第5頁
已閱讀5頁,還剩15頁未讀, 繼續(xù)免費(fèi)閱讀

下載本文檔

版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請(qǐng)進(jìn)行舉報(bào)或認(rèn)領(lǐng)

文檔簡(jiǎn)介

2025年高頻string類面試題及答案String為什么設(shè)計(jì)成不可變類?底層如何保證不可變性?String被設(shè)計(jì)為不可變類主要基于以下原因:一是線程安全,多個(gè)線程同時(shí)訪問時(shí)無需額外同步;二是支持字符串常量池優(yōu)化,相同字面量僅存儲(chǔ)一次,節(jié)省內(nèi)存;三是緩存哈希值,String的hashCode()方法會(huì)緩存計(jì)算結(jié)果,不可變性保證了哈希值不會(huì)重復(fù)計(jì)算。底層實(shí)現(xiàn)上,JDK8及之前String內(nèi)部使用`finalchar[]value`存儲(chǔ)字符,JDK9起改為`finalbyte[]value`(配合`coder`字段標(biāo)識(shí)編碼,LATIN1或UTF16),數(shù)組被final修飾確保引用不可變,且String類自身被聲明為`final`禁止繼承,所有修改操作(如concat、substring)均返回新的String對(duì)象,而非修改原對(duì)象,從根本上保證了不可變性。String、StringBuilder、StringBuffer的核心區(qū)別是什么?各自適用場(chǎng)景?三者均用于處理字符串,但特性差異顯著:String是不可變類,所有修改操作提供新對(duì)象;StringBuilder是可變類,非線程安全,內(nèi)部通過`char[]`(或`byte[]`)擴(kuò)容實(shí)現(xiàn)高效拼接;StringBuffer同樣可變,但方法使用`synchronized`修飾,保證線程安全。適用場(chǎng)景:少量字符串操作選String;單線程下大量拼接選StringBuilder(性能最高);多線程環(huán)境下的拼接操作選StringBuffer(需犧牲部分性能換取線程安全)。字符串拼接有哪些方式?性能差異如何?常見拼接方式包括:1.`+`運(yùn)算符:編譯時(shí)自動(dòng)優(yōu)化為StringBuilder(JDK5+),但循環(huán)中每次`+`會(huì)提供新的StringBuilder實(shí)例,導(dǎo)致額外開銷;2.`StringBuilder.append()`:手動(dòng)控制拼接,預(yù)分配容量時(shí)性能最優(yōu)(減少擴(kuò)容次數(shù));3.`String.concat()`:底層通過`Arrays.copyOf`復(fù)制字符數(shù)組,僅適合短字符串拼接(每次拼接提供新數(shù)組,長字符串效率低);4.`String.format()`:支持格式化,但內(nèi)部通過`newStringBuilder`實(shí)現(xiàn),性能低于直接使用StringBuilder;5.`Joiner`(Guava)或`String.join()`(JDK8+):適合多元素按分隔符拼接(如集合轉(zhuǎn)字符串)。性能排序(從高到低):StringBuilder(預(yù)分配容量)>String.concat(短字符串)>`+`(非循環(huán)場(chǎng)景)>String.format()>StringBuffer(多線程同步開銷)。`equals()`和`==`在比較字符串時(shí)的區(qū)別?`==`比較的是對(duì)象的內(nèi)存地址(引用是否相同),而`equals()`比較的是字符串內(nèi)容(String重寫了Object的equals方法)。例如:`Strings1="abc";Strings2="abc";``s1==s2`為true(指向常量池同一對(duì)象);`Strings3=newString("abc");``s1==s3`為false(s3在堆中新建對(duì)象),但`s1.equals(s3)`為true(內(nèi)容相同)。需注意,若未重寫equals方法的類(如自定義類),`==`和`equals()`行為一致(均比較引用)。`intern()`方法的作用是什么?JDK6與JDK7+的實(shí)現(xiàn)有何差異?`intern()`的核心作用是將字符串對(duì)象放入字符串常量池,并返回池中的引用。JDK6中,常量池位于永久代(PermGen),調(diào)用`intern()`時(shí)若池不存在該字符串,會(huì)復(fù)制原字符串的字符到永久代并返回其引用;JDK7+常量池移至堆內(nèi)存,若池不存在該字符串,直接存儲(chǔ)原字符串的引用(而非復(fù)制字符),此時(shí)原字符串和池中的字符串共享同一字符數(shù)組。例如:`Strings=newString("123");ern();`JDK6中,池會(huì)新建"123"對(duì)象;JDK7+中,池直接引用堆中的s對(duì)象(若池原本無"123")。這一變化減少了內(nèi)存復(fù)制,避免永久代內(nèi)存溢出問題。字符串常量池的存儲(chǔ)位置在JVM不同版本中如何變化?JDK6及之前,字符串常量池存儲(chǔ)于方法區(qū)的永久代(PermGen),受限于永久代大?。J(rèn)僅幾十MB),大量字符串字面量易導(dǎo)致`OutOfMemoryError:PermGenspace`;JDK7中,常量池從永久代遷移至堆內(nèi)存(Heap),與普通對(duì)象一樣受堆內(nèi)存管理;JDK8起,永久代被元空間(Metaspace)替代,但字符串常量池仍保留在堆中,元空間僅存儲(chǔ)類元信息等,此舉避免了永久代的內(nèi)存限制問題,提高了大字符串場(chǎng)景的穩(wěn)定性。`substring(intbeginIndex,intendIndex)`在JDK6和JDK7+中的實(shí)現(xiàn)差異?JDK6中,substring返回的新String與原String共享`char[]value`數(shù)組(通過`newString(offset+beginIndex,endIndexbeginIndex,value)`構(gòu)造),僅修改`offset`和`count`字段。這一設(shè)計(jì)可能導(dǎo)致內(nèi)存泄漏:若原字符串很大,僅需其中一小部分子串,原字符串的`value`數(shù)組仍會(huì)被子串引用無法回收。JDK7+中,substring會(huì)新建一個(gè)`char[]`數(shù)組,復(fù)制原數(shù)組中需要的字符(`Arrays.copyOfRange(value,beginIndex,endIndex)`),子串與原字符串不再共享底層數(shù)組,避免了內(nèi)存泄漏問題,但增加了復(fù)制開銷(對(duì)大字符串更友好)。如何判斷兩個(gè)字符串是否為旋轉(zhuǎn)字符串(如"abcd"和"cdab")?旋轉(zhuǎn)字符串指將原字符串的前k個(gè)字符移到末尾形成的新字符串。判斷方法:若字符串s1和s2是旋轉(zhuǎn)關(guān)系,則s2一定是s1+s1的子串,且兩者長度相同。例如:`s1="abcd";s2="cdab";``s1+s1="abcdabcd"`,包含s2("cdab"),且長度相等,故為旋轉(zhuǎn)字符串。代碼實(shí)現(xiàn):```javapublicstaticbooleanisRotation(Strings1,Strings2){if(s1==null||s2==null||s1.length()!=s2.length())returnfalse;return(s1+s1).contains(s2);}```如何高效判斷字符串是否包含重復(fù)字符?方法一(通用):使用HashSet存儲(chǔ)已出現(xiàn)的字符,遍歷字符串時(shí)檢查是否已存在,存在則返回true。時(shí)間復(fù)雜度O(n),空間復(fù)雜度O(1)(字符集有限時(shí),如ASCII最多256個(gè)字符)。方法二(位運(yùn)算,僅適用于小寫字母):用int變量的每一位表示字符是否出現(xiàn)(a對(duì)應(yīng)第0位,b對(duì)應(yīng)第1位...),遍歷字符時(shí)計(jì)算`1<<(c'a')`,若與當(dāng)前掩碼有交集則重復(fù)。代碼示例:```javapublicstaticbooleanhasDuplicate(Strings){intmask=0;for(charc:s.toCharArray()){intbit=1<<(c'a');//假設(shè)僅小寫字母if((mask&bit)!=0)returntrue;mask|=bit;}returnfalse;}```如何實(shí)現(xiàn)字符串反轉(zhuǎn)?要求至少寫出三種方法。方法一(迭代交換):將字符串轉(zhuǎn)為字符數(shù)組,首尾指針交換直至中間。```javapublicstaticStringreverse1(Strings){if(s==null)returnnull;char[]arr=s.toCharArray();inti=0,j=arr.length1;while(i<j){chartemp=arr[i];arr[i]=arr[j];arr[j]=temp;i++;j--;}returnnewString(arr);}```方法二(遞歸):遞歸截取首尾字符,中間部分反轉(zhuǎn)后拼接。```javapublicstaticStringreverse2(Strings){if(s.length()<=1)returns;returns.charAt(s.length()-1)+reverse2(s.substring(0,s.length()-1));}```方法三(StringBuilder):直接調(diào)用`reverse()`方法(最簡(jiǎn)潔)。```javapublicstaticStringreverse3(Strings){returnnewStringBuilder(s).reverse().toString();}```正則表達(dá)式在String中的常見應(yīng)用場(chǎng)景及注意事項(xiàng)?常見場(chǎng)景包括:1.`split(Stringregex)`:按正則分割字符串(如`"a,b;c".split("[,;]")`得到`["a","b","c"]`);2.`replaceAll(Stringregex,Stringreplacement)`:替換所有匹配正則的子串(如`"12a34b".replaceAll("\\d","")`得到"ab");3.`matches(Stringregex)`:判斷字符串是否完全匹配正則(如`.matches("^1[3-9]\\d{9}$")`驗(yàn)證手機(jī)號(hào))。注意事項(xiàng):正則中的特殊字符(如`.^$+?()[{\|`)需轉(zhuǎn)義(加`\\`);貪婪匹配(默認(rèn))可能匹配最長子串,非貪婪匹配(`?`修飾)匹配最短(如`"aab".replaceAll("a.?b","x")`得到"x");大字符串匹配時(shí)需注意正則復(fù)雜度(如避免指數(shù)級(jí)匹配的正則模式)。String的`hashCode()`方法如何實(shí)現(xiàn)?設(shè)計(jì)時(shí)為何選擇31作為乘數(shù)?String的hashCode()計(jì)算公式為:`hash=s[0]31^(n-1)+s[1]31^(n-2)+...+s[n-1]`,其中s[i]是字符的ASCII值,n是字符串長度。選擇31的原因:一是31是質(zhì)數(shù),減少哈希沖突(質(zhì)數(shù)的乘積更分散);二是31可被JVM優(yōu)化為位運(yùn)算(`31i=(i<<5)i`),計(jì)算效率更高。例如,`"abc"`的hashCode計(jì)算為:`'a'312+'b'31+'c'=97961+9831+99=93217+3038+99=96354`。處理大字符串(如1GB)時(shí),如何優(yōu)化內(nèi)存使用?優(yōu)化策略包括:1.避免不必要的字符串拼接:優(yōu)先使用`StringBuilder`并預(yù)分配足夠容量(`newStringBuilder(initialCapacity)`),減少擴(kuò)容次數(shù);2.利用`substring`的JDK7+特性:避免JDK6中共享數(shù)組導(dǎo)致的內(nèi)存泄漏;3.使用`intern()`共享字符串:若大量重復(fù)字符串,通過intern()將其存入常量池,減少重復(fù)存儲(chǔ)(需評(píng)估常量池內(nèi)存壓力);4.流式處理:讀取大字符串時(shí)按塊讀?。ㄈ缡褂胉BufferedReader.readLine()`),避免一次性加載到內(nèi)存;5.避免使用`String.getBytes()`提供大字節(jié)數(shù)組:改用`InputStream`或`ByteBuffer`逐塊處理;6.使用`CharBuffer`(NIO):直接操作堆外內(nèi)存,減少GC壓力。如何正確處理字符串中的轉(zhuǎn)義字符(如反斜杠、雙引號(hào))?轉(zhuǎn)義字符需通過轉(zhuǎn)義符`\`處理,具體規(guī)則:反斜杠`\`需表示為`\\`(Java字符串中`\`是轉(zhuǎn)義符,需用`\\`表示一個(gè)實(shí)際反斜杠);雙引號(hào)`"`在雙引號(hào)字符串中需表示為`\"`(如`Strings="他說:\"你好\"";`);正則中的特殊字符(如`.`)需用`\\.`匹配字面量`.`;若需處理任意特殊字符,可使用`Pattern.quote(Strings)`方法,將字符串轉(zhuǎn)為正則字面量(如`Pattern.quote("a.b")`提供`\Qa.b\E`,匹配"a.b"本身)。示例:解析JSON字符串時(shí),需將雙引號(hào)轉(zhuǎn)義為`\"`,反斜杠轉(zhuǎn)義為`\\`,否則會(huì)導(dǎo)致解析錯(cuò)誤。KMP算法的核心思想是什么?如何實(shí)現(xiàn)字符串匹配?KMP算法通過預(yù)處理模式串提供部分匹配表(next數(shù)組),避免主串指針回溯,將時(shí)間復(fù)雜度優(yōu)化到O(n+m)(n為主串長度,m為模式串長度)。next數(shù)組存儲(chǔ)的是模式串前綴和后綴的最長公共長度。實(shí)現(xiàn)步驟:1.構(gòu)建next數(shù)組:遍歷模式串,計(jì)算每個(gè)位置i的最長公共前后綴長度;2.匹配過程:主串指針i和模式串指針j同時(shí)移動(dòng),若字符不匹配則j回退到next[j-1],直到j(luò)=0或匹配;若j等于模式串長度,說明匹配成功。代碼示例(構(gòu)建next數(shù)組):```javapublicstaticint[]buildNext(Stringpattern){intm=pattern.length();int[]next=newint[m];next[0]=0;for(inti=1,len=0;i<m;){if(pattern.charAt(i)==pattern.charAt(len)){next[i++]=++len;}elseif(len>0){len=next[len1];}else{next[i++]=0;}}returnnext;}```匹配邏輯:```javapublicstaticintkmp(Stringtext,Stringpattern){int[]next=buildNext(pattern);inti=0,j=0;while(i<text.length()){if(text.charAt(i)==pattern.charAt(j)){i++;j++;if(j==pattern.length())returnij;//返回匹配起始位置}elseif(j>0){j=next[j1];}else{i++;}}return-1;//未匹配}```String對(duì)象在堆和字符串常量池中的存儲(chǔ)關(guān)系是怎樣的?字符串字面量(如`Strings="abc";`):編譯時(shí)存入常量池(JDK7+在堆中),運(yùn)行時(shí)若常量池已有該字符串,則s直接指向池中的對(duì)象;`newString("abc")`:在堆中創(chuàng)建新的String對(duì)象,同時(shí)檢查常量池是否有"abc"。若沒有,先在常量池創(chuàng)建"abc"對(duì)象(JDK6復(fù)制字符數(shù)組,JDK7+引用堆對(duì)象),然后堆中的String對(duì)象的`value`數(shù)組指向常量池的字符數(shù)組(JDK6)或直接存儲(chǔ)字符數(shù)組(JDK7+);動(dòng)態(tài)提供的字符串(如`Strings="a"+"b";`):編譯時(shí)優(yōu)化為`"ab"`,存入常量池;拼接變量(如`Stringa="a";Strings=a+"b";`):編譯時(shí)提供StringBuilder,運(yùn)行時(shí)拼接后通過`toString()`提供新的String對(duì)象(在堆中,不一定進(jìn)入常量池,除非調(diào)用intern())。為什么String常被用作HashMap的key?主要原因:1.不可變性:String的不可變性保證了hashCode()在對(duì)象創(chuàng)建后不會(huì)改變,避免HashMap中鍵的哈希值變化導(dǎo)致的索引錯(cuò)誤(若鍵可變,修改后可能無法找到原存儲(chǔ)位置);2.高效的hashCode()和equals():String重寫了這兩個(gè)方法,hashCode()基于字符內(nèi)容計(jì)算(緩存結(jié)果),equals()直接比較字符數(shù)組,保證了哈希表的查找效率;3.廣泛的使用場(chǎng)景:字符串是最常見的鍵類型(如配置項(xiàng)、標(biāo)識(shí)符等),設(shè)計(jì)為key符合實(shí)際需求。如何統(tǒng)計(jì)字符串中各字符的出現(xiàn)次數(shù)(區(qū)分大小寫和空格)?方法:使用HashMap<Character,Integer>遍歷字符串,統(tǒng)計(jì)每個(gè)字符的出現(xiàn)次數(shù)。代碼示例:```javapublicstaticMap<Character,Integer>countChar(Strings){Map<Character,Integer>map=newHashMap<>();for(charc:s.toCharArray()){map.put(c,map.getOrDefault(c,0)+1);}returnmap;}```若需按順序輸出(如字母順序),可將鍵存入TreeSet后遍歷;若字符集有限(如ASCII),可用數(shù)組替代HashMap(更高效):```javapublicstaticint[]countAscii(Strings){int[]count=newint[256];//ASCII共256個(gè)字符for(charc:s.toCharArray()){count[c]++;}returncount;}```處理超長字符串時(shí),可能遇到哪些性能瓶頸?如何解決?瓶頸及解決方案:1.內(nèi)存占用過高:超長字符串一次性加載到內(nèi)存易導(dǎo)致OOM。解決:流式處理(按行/塊讀取)、使用`java.util.stream.Stream`逐元素處理;2.GC壓力大:大量臨時(shí)String對(duì)象被創(chuàng)建,觸發(fā)頻繁GC。解決:復(fù)用StringBuilder實(shí)例、避免不必要的對(duì)象創(chuàng)建(如循環(huán)中使用`newStringBuilder`

溫馨提示

  • 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請(qǐng)下載最新的WinRAR軟件解壓。
  • 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請(qǐng)聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
  • 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁內(nèi)容里面會(huì)有圖紙預(yù)覽,若沒有圖紙預(yù)覽就沒有圖紙。
  • 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
  • 5. 人人文庫網(wǎng)僅提供信息存儲(chǔ)空間,僅對(duì)用戶上傳內(nèi)容的表現(xiàn)方式做保護(hù)處理,對(duì)用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對(duì)任何下載內(nèi)容負(fù)責(zé)。
  • 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請(qǐng)與我們聯(lián)系,我們立即糾正。
  • 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時(shí)也不承擔(dān)用戶因使用這些下載資源對(duì)自己和他人造成任何形式的傷害或損失。

最新文檔

評(píng)論

0/150

提交評(píng)論