字符串是程序開發當中,使用最頻繁的類型之一,有着與基礎類型相同的地位,甚至在JVM(Java虛擬機)編譯的時候對字符串作特殊的處理,好比拼接操做可能會被JVM直接合成一個最終的字符串,從而達到高效運行的目的.編程
字符串建立的兩種方式:緩存
查看下面代碼:安全
String s1 = "laowang"; String s2 = s1; String s3 = new String(s1); System.out.println(s1 == s2); System.out.println(s1 == s3);
輸出結果: true , false 多線程
爲何會這樣?緣由是 s3 使用 new String 時必定會在堆中從新建立一個內存區域,而 s2 則會直接使用了s1 的引用,因此獲得的結果也徹底不一樣.app
字符串拼加的幾種方式:性能
根據前面的知識咱們知道,對於String的任何操做實際上是建立了一個新對象,而後再把引用地址返回該對象,但JVM也會對String進行特殊處理,以此來提升程序的運行效率,好比如下代碼:測試
String str = "hi," + "lao" + "wang";
通過JVM優化後的代碼是這樣的:優化
String str = "hi, laowang";
驗證代碼以下:ui
String str1 = "hi," + "lao" + "wang"; String str2 = "hi,laowang"; System.out.println(str1 == str2);
執行結果: true this
這就說明JVM在某些狀況下回特殊處理String類型.
字符串截取使用 substring() 方法,使用以下:
String str = "abcdef"; // 結果: cdef (從下標爲2的開始截取到最後,包含開始下標) System.out.println(str.substring(2)); // 結果: cd (從下標爲2的開始截取到下標爲4的,包含開始下標不包含結束下標) System.out.println(str.substring(2,4));
字符串格式化可讓代碼更簡潔更直觀,好比"我叫老王,今年26歲,喜歡讀書"在這條信息中,姓名,年齡,興趣都是要動態改變的,若是使用"+"號拼接的話很容易出錯,這個時候字符串格式化方法String.format() 就派上用場了,代碼以下:
String str = String.format("我叫%s,今年%d歲,喜歡%s","老王",26,"讀書");
轉換符說明列表:
轉換符 | 說明 |
%s | 字符串類型 |
%d | 整數類型(十進制) |
%c | 字符類型 |
%b | 布爾類型 |
%x | 整數類型(十六進制) |
%o | 整數類型(八進制) |
%f | 浮點類型 |
%a | 浮點類型(十六進制) |
%e | 指數類型 |
%% | 百分比類型 |
%n | 換行符 |
根據前面的知識咱們知道,使用String 和 new String聲明的對象是不一樣的,那有沒有簡單的方法,能夠忽略他們的建立方式(有沒有new)而只對比他們的值是否相同呢? 答案是確定的,使用 equals() 方法能夠實現,代碼以下:
String s1 = "hi," + "lao" + "wang"; String s2 = "hi,"; s2 += "lao"; s2 += "wang"; String s3 = "hi,laowang"; System.out.println(s1.equals(s2)); // true System.out.println(s1.equals(s3)); // true System.out.println(s2.equals(s3)); // true
以上使用equals 對比的結果都爲true.
若是要忽略字符串的大小寫對比值可使用equalsIgoreCase(), 代碼示例:
String s1 = "Hi,laowang"; String s2 = "hi,laowang"; System.out.println(s1.equals(s2)); // false System.out.println(s1.equalsIgnoreCase(s2)); // true
字符串相關類型主要有這三種: String, StringBuffer, StringBuilder, 其中StringBuffer, StringBuilder 都是可變的字符串類型, StringBuffer在字符串拼接時使用synchronized來保障線程安全,所以在多線程字符串拼接中推薦使用StringBuffer.
StringBuffer 使用:
StringBuffer sf = new StringBuffer("lao"); // 添加字符串到尾部 sf.append("wang"); // 執行結果: laowang System.out.println(sf); // 插入字符串到當前字符串下標的位置 sf.insert(0,"hi,"); // 執行結果: hi,laowang System.out.println(sf); // 修改字符串中的某個下標的值 sf.setCharAt(0,'H'); // 執行結果: Hi,laowang System.out.println(sf);
StringBuilder 的使用方法和StringBuffer同樣,他們都繼承於AbstractStringBuilder.
答: String不是基礎數據類型,它是從堆上分配來的.基礎數據類型有8個,分別是: boolean, byte, short, int, long, float, double, char.
A: str.length
B: str.size
C: str.length()
D: str.size()
答案: C
題目解析: 字符串沒有length屬性,只有length()方法.
答: "==" 對基本類型來講是值比較,對於引用類型來講比較的是引用; 而equals默認狀況下是引用比較, 只是不少類重寫了equals方法,好比String, Integer 等把它變成了值比較,因此通常狀況下equals 比較的是值是否相等.
① "==" 解讀
對於基本類型和引用類型 == 的做用效果是不一樣的,以下所示:
代碼實例:
String x = "string"; String y = "string"; String z = new String("string"); System.out.println(x==y); // true System.out.println(x==z); // false System.out.println(x.equals(y)); // true System.out.println(x.equals(z)); // true
代碼說明: 由於x和y指向同一個引用,因此 == 也是true, 而new String()方法則從新開闢了內存空間, 因此 == 結果爲 false,而equals 比較的一直是值, 因此結果都爲 true.
② equals 解讀
equals 本質上就是 == , 只不過 String 和 Integer 等重寫了 equals 方法,把它變成了值比較. 看下面的代碼就明白了.
首先來看默認狀況下equals 比較一個有相同值得對象,代碼以下:
class Cat{ private String name; public Cat(String name) { this.name = name; } public String getName() { return name; } public void setName(String name) { this.name = name; } } Cat c1 = new Cat("薰悟空"); Cat c2 = new Cat("薰悟空"); System.out.println(c1.equals(c2)); // false
源碼以下:
public boolean equals(Object obj) { return (this == obj); }
原來equals 本質上就是 == .
那麼問題來了, 兩個相同值的 String 對象, 爲何返回的是 true 呢? 代碼以下:
String s1 = new String("薰悟空"); String s2 = new String("薰悟空"); System.out.println(s1.equals(s2)); // true
一樣的,咱們進入String的equals 方法, 找到了答案, 代碼以下:
public boolean equals(Object anObject) { if (this == anObject) { return true; } if (anObject instanceof String) { String anotherString = (String)anObject; int n = value.length; if (n == anotherString.value.length) { char v1[] = value; char v2[] = anotherString.value; int i = 0; while (n-- != 0) { if (v1[i] != v2[i]) return false; i++; } return true; } } return false; }
原來是String 重寫了Object的equals方法,把引用比較改爲了值比較.
總結來講, "==" 對於基本類型來講是值比較, 對於引用類型來講比較的是引用; 而 equas 默認狀況下是 引用比較, 只是不少類重寫了 equas 方法, 好比 String , Integer 等把它變成了值比較, 因此通常狀況下 equals 比較的是值是否相等.
String str = "laowang"; str.substring(0,1); System.out.println(str);
A: l
B: a
C: la
D: laowang
答: D
題目解析: 由於 String 的substring() 方法不會修改原字符串內容, 因此結果仍是 laowang.
String s1 = "hi," + "lao" + "wang"; String s2 = "hi,"; s2 += "lao"; s2 += "wang"; String s3 = "hi,laowang"; System.out.println(s1 == s2); System.out.println(s1 == s3); System.out.println(s2 == s3);
答: false true false
題目解析: String s1 = "hi," + "lao" + "wang" 代碼會被JVM優化爲: String s1 = "hi,laowang", 這樣就和s3 徹底相同, s1 建立的時候會把字符串"hi,laowang" 放入常量池, s3 建立的時候,常量池中已經存在對應的緩存,會直接把引用返回給s3 , 因此 s1 == s3 就爲 true, 而 s2 使用了 += 其引用地址就和其餘兩個不一樣.
public static void main(String[] args) { String str = new String("laowang"); change(str); System.out.println(str); } public static void change(String str){ str = "xiaowang"; }
答: laowang
7.如下StringBuffer 傳值修改後的執行結果是什麼?
public static void main(String[] args) { StringBuffer sf = new StringBuffer("hi,"); change(sf); System.out.println(sf); } public static void change(StringBuffer sf){ sf.append("laowang"); }
答: hi,laowang
題目解析: String 爲不可變類型, 在方法內對String修改的時候, 至關於修改傳遞過來的是一個String 的副本, 因此String 自己的值是不被修改的, 而 StringBuffer 爲可變類型, 參數傳遞過來的是對象的引用, 對其修改它自己就會發生改變.
String str = "abcdef";
System.out.println(str.substring(3,3));
答: "" (空)
答:如下是String, StringBuffer, StringBuilder 的區別:
答: intern() 方法用於查找常量池中是否存在該字符值, 若是常量池中不存在則先在常量池中建立, 若是已經存在則直接返回.
示例代碼:
String s = "laowang"; String s2 = s.intern(); System.out.println(s == s2); // true
答: 總共建立了兩個對象, 一個是字符串"laowang", 另外一個是指向字符串的變量 s. new String() 無論常量池有沒有相同的字符串, 都會在內存(非字符串常量池)中建立一個新的對象.
字符串常量池是存儲在Java堆內存中的字符串池, 是爲防止每次新建字符串帶的時間和空間消耗的一種解決方案. 在建立字符串時JVM會首先檢查字符串常量池,若是字符串已經存在池中,就返回池中的實例引用, 若是字符串不在池中, 就會實例化一個字符串放到池中並把當前引用指向該字符串.
答: 不可變的好處以下:
答: String不能被繼承. 由於String 被聲明爲final (最終類), 因此不能被繼承.