摘要:閱讀原文面試別再問我了字符串廣泛應(yīng)用在編程中,在中字符串屬于對象,提供了類來創(chuàng)建和操作字符串。測試此字符串是否以指定的后綴結(jié)束。當(dāng)執(zhí)行此句時,因為對應(yīng)的實例已經(jīng)存在于字符串常量池中,所以會將此實例復(fù)制到會在堆中并返回引用地址。
閱讀原文:面試別再問我String了
字符串廣泛應(yīng)用 在Java 編程中,在 Java 中字符串屬于對象,Java 提供了 String 類來創(chuàng)建和操作字符串。
String 簡介String定義:
public final class String implements java.io.Serializable, Comparable為什么設(shè)計為不可變類呢?, CharSequence {}
String設(shè)計為不可變類主要考慮到:效率和安全。
效率:1.在早期的JVM實現(xiàn)版本中,被final修飾的方法會被轉(zhuǎn)為內(nèi)嵌調(diào)用以提升執(zhí)行效率。而從Java SE5/6開始,就漸漸擯棄這種方式了。因此在現(xiàn)在的Java SE版本中,不需要考慮用final去提升方法調(diào)用效率。只有在確定不想讓該方法被覆蓋時,才將方法設(shè)置為final。2.緩存hashcode,String不可變,所以hashcode不變,這樣緩存才有意義,不必重新計算。
安全:String常被作為網(wǎng)絡(luò)連接,文件操作等參數(shù)類型,倘若可改變,會出現(xiàn)意想不到的結(jié)果。
測試掌握程度為了不浪費(fèi)你的時間,請看下面的題目,若你一目了然,可以跳過本文了。
public class Test { public static void main(String[] args) { String str1 = "HelloFlyapi"; String str2 = "HelloFlyapi"; String str3 = new String("HelloFlyapi"); String str4 = "Hello"; String str5 = "Flyapi"; String str6 = "Hello" + "Flyapi"; String str7 = str4 + str5; System.out.println("str1 == str2 result: " + (str1 == str2)); System.out.println("str1 == str3 result: " + (str1 == str3)); System.out.println("str1 == str6 result: " + (str1 == str6)); System.out.println("str1 == str7 result: " + (str1 == str7)); System.out.println("str1 == str7.intern() result: " + (str1 == str7.intern())); System.out.println("str3 == str3.intern() result: " + (str3 == str3.intern())); } }String 的創(chuàng)建方式
從上面的題中你會知道,String的創(chuàng)建方式有兩種:
直接賦值
此方式在方法區(qū)中字符串常量池中創(chuàng)建對象
String str = "flyapi";構(gòu)造器
此方式在堆內(nèi)存創(chuàng)建對象
String str = new String();分析
要理解String,那么要了解JVM內(nèi)存中的棧(stack)、堆(heap)和方法區(qū)。簡要圖如下:
str1 == str2
String str1 = "HelloFlyapi"; String str2 = "HelloFlyapi"; System.out.println(str1 == str2); // true
當(dāng)執(zhí)行第一句時,JVM會先去常量池中查找是否存在HelloFlyapi,當(dāng)存在時直接返回常量池里的引用;當(dāng)不存在時,會在字符創(chuàng)常量池中創(chuàng)建一個對象并返回引用。
當(dāng)執(zhí)行第二句時,同樣的道理,由于第一句已經(jīng)在常量池中創(chuàng)建了,所以直接返回上句創(chuàng)建的對象的引用。
str1 == str3
String str1 = "HelloFlyapi"; String str3 = new String("HelloFlyapi"); System.out.println(str1 == str3); // false
執(zhí)行第一句,同上第一句。
執(zhí)行第二句時,會在堆(heap)中創(chuàng)建一個對象,當(dāng)字符創(chuàng)常量池中沒有‘HelloFlyapi’時,會在常量池中也創(chuàng)建一個對象;當(dāng)常量池中已經(jīng)存在了,就不會創(chuàng)建新的了。
str1 == str6
String str1 = "HelloFlyapi"; String str6 = "Hello" + "Flyapi"; System.out.println(str1 == str6); // true
由于"Hello"和"Flyapi"都是常量,編譯時,第二句會被自動編譯為‘String str6 = "HelloFlyapi";’
str1 == str7
String str1 = "HelloFlyapi"; String str4 = "Hello"; String str5 = "Flyapi"; String str7 = str4 + str5; System.out.println(str1 == str7); // false
其中前三句變量存儲的是常量池中的引用地址。
第四句執(zhí)行時,JVM會在堆(heap)中創(chuàng)建一個以str4為基礎(chǔ)的一個StringBuilder對象,然后調(diào)用StringBuilder的append()方法完成與str5的合并,之后會調(diào)用toString()方法在堆(heap)中創(chuàng)建一個String對象,并把這個String對象的引用賦給str7。
常用方法下面是 String 類支持的方法,更多詳細(xì),參看 Java String API 文檔:
方法 | 描述 |
---|---|
char charAt(int index) | 返回指定索引處的 char 值。 |
int compareTo(Object o) | 把這個字符串和另一個對象比較。 |
int compareTo(String anotherString) | 按字典順序比較兩個字符串。 |
boolean endsWith(String suffix) | 測試此字符串是否以指定的后綴結(jié)束。 |
boolean equals(Object anObject) | 將此字符串與指定的對象比較。 |
boolean equalsIgnoreCase(String anotherString) | 將此 String 與另一個 String 比較,不考慮大小寫。 |
byte[] getBytes() | 使用平臺的默認(rèn)字符集將此 String 編碼為 byte 序列,并將結(jié)果存儲到一個新的 byte 數(shù)組中。 |
byte[] getBytes(String charsetName) | 使用指定的字符集將此 String 編碼為 byte 序列,并將結(jié)果存儲到一個新的 byte 數(shù)組中。 |
int indexOf(int ch) | 返回指定字符在此字符串中第一次出現(xiàn)處的索引。 |
int indexOf(int ch, int fromIndex) | 返回在此字符串中第一次出現(xiàn)指定字符處的索引,從指定的索引開始搜索。 |
int indexOf(String str) | 返回指定子字符串在此字符串中第一次出現(xiàn)處的索引。 |
int indexOf(String str, int fromIndex) | 返回指定子字符串在此字符串中第一次出現(xiàn)處的索引,從指定的索引開始。 |
String intern() | 返回字符串對象的規(guī)范化表示形式。 |
int lastIndexOf(int ch) | 返回指定字符在此字符串中最后一次出現(xiàn)處的索引。 |
int lastIndexOf(int ch, int fromIndex) | 返回指定字符在此字符串中最后一次出現(xiàn)處的索引,從指定的索引處開始進(jìn)行反向搜索。 |
int lastIndexOf(String str) | 返回指定子字符串在此字符串中最右邊出現(xiàn)處的索引。 |
int lastIndexOf(String str, int fromIndex) | 返回指定子字符串在此字符串中最后一次出現(xiàn)處的索引,從指定的索引開始反向搜索。 |
int length() | 返回此字符串的長度。 |
boolean matches(String regex) | 告知此字符串是否匹配給定的正則表達(dá)式。 |
String replace(char oldChar, char newChar) | 返回一個新的字符串,它是通過用 newChar 替換此字符串中出現(xiàn)的所有 oldChar 得到的。 |
String replaceAll(String regex, String replacement) | 使用給定的 replacement 替換此字符串所有匹配給定的正則表達(dá)式的子字符串。 |
String replaceFirst(String regex, String replacement) | 使用給定的 replacement 替換此字符串匹配給定的正則表達(dá)式的第一個子字符串。 |
String[] split(String regex) | 根據(jù)給定正則表達(dá)式的匹配拆分此字符串。 |
String[] split(String regex, int limit) | 根據(jù)匹配給定的正則表達(dá)式來拆分此字符串。 |
boolean startsWith(String prefix) | 測試此字符串是否以指定的前綴開始。 |
boolean startsWith(String prefix, int toffset) | 測試此字符串從指定索引開始的子字符串是否以指定前綴開始。 |
String substring(int beginIndex) | 返回一個新的字符串,它是此字符串的一個子字符串。 |
String substring(int beginIndex, int endIndex) | 返回一個新字符串,它是此字符串的一個子字符串。 |
char[] toCharArray() | 將此字符串轉(zhuǎn)換為一個新的字符數(shù)組。 |
String toLowerCase() | 使用默認(rèn)語言環(huán)境的規(guī)則將此 String 中的所有字符都轉(zhuǎn)換為小寫。 |
String toUpperCase() | 使用默認(rèn)語言環(huán)境的規(guī)則將此 String 中的所有字符都轉(zhuǎn)換為大寫。 |
String trim() | 返回字符串的副本,忽略前導(dǎo)空白和尾部空白。 |
由于String的不可變性導(dǎo)致,字符串變更時效率低下,在之后得JDK版本中出現(xiàn)了StringBuilder和StringBuffer.
類 | 可變性 | 線程安全 |
---|---|---|
String | 不可變 | 安全 |
StringBuffer | 可變 | 安全 |
StringBuilder | 可變 | 非安全 |
使用選擇
當(dāng)有少量連接操作時,使用String
當(dāng)單線程下有大量連接操作時,使用StringBuilder
當(dāng)多線程下有大量連接操作時,使用StringBuffer
常見String面試題String str = new String("abc")創(chuàng)建了多少個實例?
這個問題其實是不嚴(yán)謹(jǐn)?shù)?,但面試一般會遇到,所以我們要補(bǔ)充來說明。
類的加載和執(zhí)行要分開來講:
創(chuàng)建了兩個當(dāng)加載類時,"abc"被創(chuàng)建并駐留在了字符創(chuàng)常量池中(如果先前加載中沒有創(chuàng)建駐留過)。
當(dāng)執(zhí)行此句時,因為"abc"對應(yīng)的String實例已經(jīng)存在于字符串常量池中,所以JVM會將此實例復(fù)制到會在堆(heap)中并返回引用地址。
通過字節(jié)碼我們可以看到:
源碼:String str = new String("abc")
字節(jié)碼:
Code: 0: new #2 // class java/lang/String 3: dup 4: ldc #3 // String abc 6: invokespecial #4 // Method java/lang/String."":(Ljava/lang/String;) 9: astore_1 10: return
執(zhí)行時僅(#2)創(chuàng)建了一個對象。
關(guān)于這個面試題,可以看看一個超大牛的回答:http://rednaxelafx.iteye.com/...
本文優(yōu)先發(fā)布于微信公眾號:碼上實戰(zhàn) 和 GitHub 上。
GitHub flyhero
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://systransis.cn/yun/73991.html
摘要:例如資源的獲取,支持多種消息例如的支持,對多了工具級別的支持等待。最上面的知道吧我就不講了。生命周期事件回調(diào)等。他支持不同信息源頭,支持工具類,支持層級容器,支持訪問文件資源,支持事件發(fā)布通知,支持接口回調(diào)等等。 廣義的 IOC IoC(Inversion of Control) 控制反轉(zhuǎn),即不用打電話過來,我們會打給你。 兩種實現(xiàn): 依賴查找(DL)和依賴注入(DI)。 IOC 和...
摘要:閱讀原文小而有料的想聽文章核心內(nèi)容語音對話版,請到微信公眾號碼上實戰(zhàn),閱讀原文這塊肉雖然小,但不可不知,因為不僅面試可能會問,實際中也常使用。參考書籍編程思想深入理解虛擬機(jī)更多精彩技術(shù)文章盡在微信公眾號碼上實戰(zhàn) 閱讀原文:小而有料的final(想聽文章核心內(nèi)容語音對話版,請到微信公眾號——碼上實戰(zhàn),閱讀原文) final這塊肉雖然小,但不可不知,因為不僅面試可能會問,實際中也常使用。就...
摘要:所以構(gòu)造函數(shù)里的指的就是將要被出來的新對象。希望看完這篇文章之后,再有人問指向的問題,你可以嘴角微微上揚(yáng),冷笑一聲不要再問我的指向問題了。 this的指向已經(jīng)是一個老生常談的問題,每逢面試都要去復(fù)習(xí)復(fù)習(xí),近來鞏固js的基礎(chǔ),決心徹底掌握這個知識點(diǎn),一勞永逸。說明一下,為了不影響大家的思考過程,下面的代碼都不會去注釋答案,想知道答案,只需要去控制臺執(zhí)行一下。 四類場景逐一擊破 首先,分析...
摘要:不過大多數(shù)講解還停留在對功能使用的層面,其底層的很多原理,很多人可能并不知曉。每個線程池里的線程就僅僅用于請求那個服務(wù)。 歡迎關(guān)注微信公眾號:石杉的架構(gòu)筆記(id:shishan100) 每日更新!精品技術(shù)文章準(zhǔn)時送上! 目錄 一、業(yè)務(wù)場景介紹 二、Spring Cloud核心組件:Eureka 三、Spring Cloud核心組件:Feign 四、Spring Cloud核心組件:R...
摘要:不過大多數(shù)講解還停留在對功能使用的層面,其底層的很多原理,很多人可能并不知曉。每個線程池里的線程就僅僅用于請求那個服務(wù)。 歡迎關(guān)注微信公眾號:石杉的架構(gòu)筆記(id:shishan100) 每日更新!精品技術(shù)文章準(zhǔn)時送上! 目錄 一、業(yè)務(wù)場景介紹 二、Spring Cloud核心組件:Eureka 三、Spring Cloud核心組件:Feign 四、Spring Cloud核心組件:R...
閱讀 1964·2021-09-07 09:59
閱讀 2528·2019-08-29 16:33
閱讀 3701·2019-08-29 16:18
閱讀 2860·2019-08-29 15:30
閱讀 1687·2019-08-29 13:52
閱讀 2050·2019-08-26 18:36
閱讀 544·2019-08-26 12:19
閱讀 707·2019-08-23 15:23