字串廣泛應用 在Java 程式設計中,在 Java 中字串屬於物件,Java 提供了 String 類來建立和操作字串。
String 簡介
String定義:
public final class String
implements java.io.Serializable, Comparable<String>, CharSequence {}
複製程式碼
為什麼設計為不可變類呢?
String設計為不可變類主要考慮到:效率和安全。
- 效率:1.在早期的JVM實現版本中,被final修飾的方法會被轉為內嵌呼叫以提升執行效率。而從Java SE5/6開始,就漸漸擯棄這種方式了。因此在現在的Java SE版本中,不需要考慮用final去提升方法呼叫效率。只有在確定不想讓該方法被覆蓋時,才將方法設定為final。2.快取hashcode,String不可變,所以hashcode不變,這樣快取才有意義,不必重新計算。
- 安全:String常被作為網路連線,檔案操作等引數型別,倘若可改變,會出現意想不到的結果。
測試掌握程度
為了不浪費你的時間,請看下面的題目,若你一目瞭然,可以跳過本文了。
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 的建立方式
從上面的題中你會知道,String的建立方式有兩種:
直接賦值
- 此方式在方法區中字串常量池中建立物件
String str = "flyapi"; 複製程式碼
構造器
-
此方式在堆記憶體建立物件
String str = new String(); 複製程式碼
分析
要理解String,那麼要了解JVM記憶體中的棧(stack)、堆(heap)和方法區。簡要圖如下:
-
str1 == str2
String str1 = "HelloFlyapi"; String str2 = "HelloFlyapi"; System.out.println(str1 == str2); // true 複製程式碼
當執行第一句時,JVM會先去常量池中查詢是否存在HelloFlyapi,當存在時直接返回常量池裡的引用;當不存在時,會在字元創常量池中建立一個物件並返回引用。
當執行第二句時,同樣的道理,由於第一句已經在常量池中建立了,所以直接返回上句建立的物件的引用。
-
str1 == str3
String str1 = "HelloFlyapi"; String str3 = new String("HelloFlyapi"); System.out.println(str1 == str3); // false 複製程式碼
執行第一句,同上第一句。
執行第二句時,會在堆(heap)中建立一個物件,當字元創常量池中沒有‘HelloFlyapi’時,會在常量池中也建立一個物件;當常量池中已經存在了,就不會建立新的了。
-
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 複製程式碼
其中前三句變數儲存的是常量池中的引用地址。
第四句執行時,JVM會在堆(heap)中建立一個以str4為基礎的一個StringBuilder物件,然後呼叫StringBuilder的append()方法完成與str5的合併,之後會呼叫toString()方法在堆(heap)中建立一個String物件,並把這個String物件的引用賦給str7。
常用方法
下面是 String 類支援的方法,更多詳細,參看 Java String API 文件:
方法 | 描述 |
---|---|
char charAt(int index) | 返回指定索引處的 char 值。 |
int compareTo(Object o) | 把這個字串和另一個物件比較。 |
int compareTo(String anotherString) | 按字典順序比較兩個字串。 |
boolean endsWith(String suffix) | 測試此字串是否以指定的字尾結束。 |
boolean equals(Object anObject) | 將此字串與指定的物件比較。 |
boolean equalsIgnoreCase(String anotherString) | 將此 String 與另一個 String 比較,不考慮大小寫。 |
byte[] getBytes() | 使用平臺的預設字符集將此 String 編碼為 byte 序列,並將結果儲存到一個新的 byte 陣列中。 |
byte[] getBytes(String charsetName) | 使用指定的字符集將此 String 編碼為 byte 序列,並將結果儲存到一個新的 byte 陣列中。 |
int indexOf(int ch) | 返回指定字元在此字串中第一次出現處的索引。 |
int indexOf(int ch, int fromIndex) | 返回在此字串中第一次出現指定字元處的索引,從指定的索引開始搜尋。 |
int indexOf(String str) | 返回指定子字串在此字串中第一次出現處的索引。 |
int indexOf(String str, int fromIndex) | 返回指定子字串在此字串中第一次出現處的索引,從指定的索引開始。 |
String intern() | 返回字串物件的規範化表示形式。 |
int lastIndexOf(int ch) | 返回指定字元在此字串中最後一次出現處的索引。 |
int lastIndexOf(int ch, int fromIndex) | 返回指定字元在此字串中最後一次出現處的索引,從指定的索引處開始進行反向搜尋。 |
int lastIndexOf(String str) | 返回指定子字串在此字串中最右邊出現處的索引。 |
int lastIndexOf(String str, int fromIndex) | 返回指定子字串在此字串中最後一次出現處的索引,從指定的索引開始反向搜尋。 |
int length() | 返回此字串的長度。 |
boolean matches(String regex) | 告知此字串是否匹配給定的正規表示式。 |
String replace(char oldChar, char newChar) | 返回一個新的字串,它是通過用 newChar 替換此字串中出現的所有 oldChar 得到的。 |
String replaceAll(String regex, String replacement) | 使用給定的 replacement 替換此字串所有匹配給定的正規表示式的子字串。 |
String replaceFirst(String regex, String replacement) | 使用給定的 replacement 替換此字串匹配給定的正規表示式的第一個子字串。 |
String[] split(String regex) | 根據給定正規表示式的匹配拆分此字串。 |
String[] split(String regex, int limit) | 根據匹配給定的正規表示式來拆分此字串。 |
boolean startsWith(String prefix) | 測試此字串是否以指定的字首開始。 |
boolean startsWith(String prefix, int toffset) | 測試此字串從指定索引開始的子字串是否以指定字首開始。 |
String substring(int beginIndex) | 返回一個新的字串,它是此字串的一個子字串。 |
String substring(int beginIndex, int endIndex) | 返回一個新字串,它是此字串的一個子字串。 |
char[] toCharArray() | 將此字串轉換為一個新的字元陣列。 |
String toLowerCase() | 使用預設語言環境的規則將此 String 中的所有字元都轉換為小寫。 |
String toUpperCase() | 使用預設語言環境的規則將此 String 中的所有字元都轉換為大寫。 |
String trim() | 返回字串的副本,忽略前導空白和尾部空白。 |
String相關
由於String的不可變性導致,字串變更時效率低下,在之後得JDK版本中出現了StringBuilder和StringBuffer.
類 | 可變性 | 執行緒安全 |
---|---|---|
String | 不可變 | 安全 |
StringBuffer | 可變 | 安全 |
StringBuilder | 可變 | 非安全 |
- 使用選擇
- 當有少量連線操作時,使用String
- 當單執行緒下有大量連線操作時,使用StringBuilder
- 當多執行緒下有大量連線操作時,使用StringBuffer
常見String面試題
- String str = new String("abc")建立了多少個例項?
這個問題其實是不嚴謹的,但面試一般會遇到,所以我們要補充來說明。
類的載入和執行要分開來講: 建立了兩個
- 當載入類時,"abc"被建立並駐留在了字元創常量池中(如果先前載入中沒有建立駐留過)。
- 當執行此句時,因為"abc"對應的String例項已經存在於字串常量池中,所以JVM會將此例項複製到會在堆(heap)中並返回引用地址。
通過位元組碼我們可以看到:
原始碼:String str = new String("abc")
位元組碼:
Code:
0: new #2 // class java/lang/String
3: dup
4: ldc #3 // String abc
6: invokespecial #4 // Method java/lang/String."<init>":(Ljava/lang/String;)
9: astore_1
10: return
複製程式碼
執行時僅(#2)建立了一個物件。
關於這個面試題,可以看看一個超大牛的回答:rednaxelafx.iteye.com/blog/774673
給大家整理了一些面試文件和視訊,公眾號回覆:架構師 或 面試視訊
本文優先釋出於微信公眾號:碼上實戰