String類
執行個體化String對象
String 對象初始化方式有多種。
如下代碼中,各種初始化方式的效果是一樣的,初始化後,String 對象的内容為 "hello" 。
public static void main(String[] args) {
// 直接指派
String str1 = "hello";
// 構造函數方式,參數為 String
String str2 = new String("hello");
// 構造函數方式,參數為 StringBuilder
String str3 = new String(new StringBuilder("hello"));
// 構造函數方式,參數為 StringBuffer
String str4 = new String(new StringBuffer("hello"));
// 構造函數方式,參數為 char 數組
char[] cz = {'h', 'e', 'l', 'l', 'o'};
String str5 = new String(cz);
// 構造函數方式,參數為 byte 數組
byte[] bz = {'h', 'e', 'l', 'l', 'o'};
String str6 = new String(bz);
// 先使用預設構造函數,再指派
String str7 = new String();
str7 = "hello";
}
以上方式可歸納為兩類:
(1) 指派方式
(2) 構造函數方式
目前的Java SE8中,String類有15種構造函數,詳細看參見Java API。
傳入的參數可以是String、StringBuilder、StringBuffer、char 數組、byte 數組等等。
兩種執行個體化方式比較
一個字元串就是一個 String 類的匿名對象。
匿名對象
匿名對象就是開辟了記憶體空間的并且可以直接使用的對象。
對于這樣的代碼:
String name = "Jack"; // 指派方式初始化 String 對象
實際上就是在堆中開辟一個記憶體空間,這個空間的中存儲的值為 "Jack"。然後這個空間被 name 變量所引用。
注:在JAVA中,如果一個字元串已經被一個名稱所引用,則以後再有相同的字元串聲明時,不會重新開辟空間,而是複用之前的空間。這樣減少了不必要的空間開銷。
例
String str1 = "hello";
String str2 = "hello";
String str3 = "hello";
以上三個String類變量本身是存放在棧記憶體中,但是它們指向同一塊堆記憶體空間。
而如果使用構造函數方式初始化String類對象,和所有普通類一樣,隻要new一次,就會新開辟一塊堆空間。
綜上所述,可以看出指派方式要優于構造函數方式。
String的内容比較
1、使用 "=="
String str3 = str2;
System.out.println("str1 == str2 --> " + (str1 == str2));
System.out.println("str1 == str3 --> " + (str1 == str3));
System.out.println("str2 == str3 --> " + (str2 == str3));
運作結果
str1 == str2 --> false
str1 == str3 --> false
str2 == str3 --> true
從以上代碼可以看出,雖然三個字元串内容完全一緻,但是使用 "==" 去比較卻發現并不完全相等。
這是因為每個String對象的内容實際上是儲存在堆記憶體中的。是以,即使堆中的内容一緻,并不代表它們的位址空間也一緻。
"==" 是用來進行數值比較的,是以 str1 和 str2 并不相等。
2、使用equals方法
如果要比較兩個字元串的内容是否相等,可以使用 equals 方法。
System.out.println("str1 == str2 --> " + (str1.equals(str2)));
System.out.println("str1 == str3 --> " + (str1.equals(str3)));
System.out.println("str2 == str3 --> " + (str2.equals(str3)));
str1 == str2 --> true
str1 == str3 --> true
String對象不可變
//: StringDemo03.java
public class StringDemo03 {
public static String upcase(String s) {
return s.toUpperCase();
}
public static void main(String[] args) {
String a = "hello";
System.out.println(a); // hello
String b = upcase(a);
System.out.println(b); // HELLO
} /* Output:
hello
HELLO
*///:~
當把a傳給 upcase() 方法時,實際傳遞的是引用的一個拷貝。其實,每當把 String 對象作為方法的參數時,都會複制一份引用,而該引用所指的對象其實一直待在單一的實體位置上,從未動過。
是以指向 String 的任何引用都不可能改變它的值。
不可變性會帶來一定的效率問題。例如String類的重載操作符 "+"。
注:JAVA不同于C++,并不允許程式員自定義任何重載操作符。用于String的 "+" 和 "+=" 是JAVA中僅有的兩個重載操作符。
操作符"+"可以用來拼接String。方式如下:
String s = "How " + "are " + "you?";
System.out.println(s);
/* Output:
How are you?
這種方式的問題在于,會産生一大堆需要垃圾回收的中間對象。
那麼,如何避免這種問題呢?
JAVA中提供了兩個類:StringBuilder 和 StringBuffer ,它們都有 append() 方法,效率高于 String 的 "+"。
這兩個類的差别在于 StringBuffer 是線程安全的,是以開銷也更大一些。
String類的常用方法
以下代碼是String類的一些常用方法。
public class StringDemo {
// 擷取字元串字元個數
System.out.println(" Goodbye ".length());
// 擷取 String 中該索引位置上的 char
System.out.println("Computer".charAt(4));
// 複制 byte 到一個目标數組
byte bytes[] = "Winter".getBytes(); // 将字元串轉為 byte 數組
System.out.println(new String(bytes)); // 将完整 byte 數組轉為字元串
System.out.println(new String(bytes, 1, 3)); // 将部分 byte 數組轉為字元串
// 複制 char 到一個目标數組
char chars[] = new char[10];
"Summer".getChars(0, 6, chars, 2); // 将字元串0~6位置的内容拷貝到 char 數組中,從數組位置2開始
System.out.println(new String(chars)); // 将完整 char 數組轉為字元串
System.out.println(new String(chars, 1, 3)); // 将部分 char 數組轉為字元串
// 字元串轉char數組
char[] data1 = "Baby".toCharArray();
for (char c : data1) {
System.out.print(c + " ");
}
System.out.println();
// 如果String不包含此參數,傳回-1,否則傳回此參數在String中的起始索引。lastIndexOf是從後向前查找
System.out.println("How are you".indexOf("o")); // 查找傳回位置
System.out.println("How are you".indexOf("o", 5)); // 查找傳回位置, 從位置5開始
System.out.println("How are you".indexOf("z")); // 沒有查到傳回-1
System.out.println("How are you".lastIndexOf("o")); // 查找傳回位置
System.out.println("How are you".lastIndexOf("o", 5)); // 查找傳回位置, 從位置5開始
System.out.println("How are you".lastIndexOf("z")); // 沒有查到傳回-1
// 根據參數截取字元串
System.out.println("Hello World".substring(6)); // 從位置6開始截取
System.out.println("Hello World".substring(0, 5)); // 截取0~5個位置的内容
// 按照指定字元拆分字元串
String[] s = "[email protected]".split("@");
for (int i = 0; i < s.length; i++) {
System.out.println(s[i]);
// 去除左右空格
System.out.println(" Night ".trim()); // 去除左右空格輸出
// 轉換大小寫
System.out.println("China".toLowerCase());
System.out.println("China".toUpperCase());
// 判斷是否以指定的字元串開頭或結尾
if ("**NAME".startsWith("**")) {
System.out.println("**NAME 以**開頭");
if ("NAME**".endsWith("**")) {
System.out.println("NAME** 以**結尾");
// 替換源子字元串為目标子字元串
System.out.println("good".replaceAll("o", "x"));
StringBuilder類
注意
String類是不可改變的,是以你一旦建立了String對象,那它的值就無法改變了。 如果需要對字元串做很多修改,那麼應該選擇使用StringBuffer & StringBuilder 類。
StringBuilder 類的用法大部分與 String 類相似。
StringBuilder 類的字元串連接配接操作 append() 效率高于 String 類。而且此方法傳回一個 StringBuilder 類的執行個體,這樣就可以采用鍊式方式一直調用 append() 方法。
示例代碼如下:
StringBuilder str = new StringBuilder();
str.append("How ").append("are ").append("you?");
System.out.println(str);
StringBuffer類
StringBuffer 類和 StringBuilder 類大緻相同。
但是 StringBuffer 是線程安全的,是以開銷也更大一些。
參考資料
JAVA 程式設計思想
JAVA 開發實戰經典