[本節目標]
1.認識String類
2.了解String類的基本用法
3.熟練掌握String類的常見操作
4.認識字符串常量池
5.認識StringBuffer和StringBuilder
1.String類的重要性
在C語言中已經涉及到字符串了,但是在C語言中要表示字符串只能使用字符數組或者字符指針,可以使用標準庫提
供的字符串系列函數完成大部分操作,但是這種將數據和操作數據方法分離開的方式不符合面相對象的思想,而字
符串應用又非常廣泛,因此Java語言專門提供了String類。
在開發和校招筆試中,字符串也是常客,比如:
字符串轉整形數字
字符串相加
而且在面試中也頻繁被問到,比如:String、StringBuff和StringBulider之間的區別等。
2.常用方法
2.1. 字符串構造
String類提供的構造方式非常多,常用的就以下三種:
public class Test8 {public static void main(String[] args) {//使用常量串構造String s1 = "hello shousidaima";System.out.println(s1);//直接newString對象String s2 = new String("hello shousidaima");System.out.println(s2);//使用字符數組進行構造char[] arr = {'h','e','l','l','o'};String s3 = new String(arr);System.out.println(s3);}
}
[注意]
1. String是引用類型,內部并不存儲字符串本身,在String類的實現源碼中,String類實例變量如下:
// s1和s2引用的是不同對象 s1和s3引用的是同一對象
String s1 = new String("hello");
String s2 = new String("world");
String s3 = s1;System.out.println(s1.length()); // 獲取字符串長度---輸出5
System.out.println(s1.isEmpty()); // 如果字符串長度為0,返回true,否則返回false
2.在java中" "引起來的也是String類型對象.
System.out.println("hello".length()); //打印長度
2.2 String對象的比較
字符串的比較是常見操作之一,比如:字符串排序。Java中總共提供了4中方式
1. == 比較是否引用同一個對象
對象:對于內置類型, ==比較的是變量中的值:對于引用類型==比較的是引用地址.
int a = 10;
int b = 20;
int c = 10;
// 對于基本類型變量,==比較兩個變量中存儲的值是否相同
System.out.println(a == b); // false
System.out.println(a == c); // true
// 對于引用類型變量,==比較兩個引用變量引用的是否為同一個對象
String s1 = new String("hello");
String s2 = new String("hello");
String s3 = new String("world");
String s4 = s1;
System.out.println(s1 == s2);// false
System.out.println(s2 == s3); // false
System.out.println(s1 == s4); // true
2.boolean equals(Object anObject) 方法:按照字典序比較
字典序:字符大小的順序
String類重寫了父類Object中equals方法,Object中equals默認按照==比較,String重寫equals后,按照如下規則比較,比如:s1.equals(s2)? ?注意!這是java重寫的,不是讓我們自己重寫
@Overridepublic boolean equals(Object anObject) {
// 1. 先檢測this 和 anObject 是否為同一個對象比較,如果是返回true
if (this == anObject) {
return true;
}// 2. 檢測anObject是否為String類型的對象,如果是繼續比較,否則返回false
if (anObject instanceof String) {
// 將anObject向下轉型為String類型對象
String anotherString = (String)anObject;
int n = value.length;// 3. this和anObject兩個字符串的長度是否相同,是繼續比較,否則返回false
if (n == anotherString.value.length) {
char v1[] = value;
char v2[] = anotherString.value;
int i = 0;// 4. 按照字典序,從前往后逐個字符進行比較
while (n-- != 0) {
if (v1[i] != v2[i])
return false;
i++;
}
return true;
}
}
return false;}}
3.int compartTo(String s)方法:按照字典序進行比較
與equals不同的是,equals返回的是boolean類型,而compareTo返回的是int類型。具體比較方式:
1. 先按照字典次序大小比較,如果出現不等的字符,直接返回這兩個字符的大小差值
2. 如果前k個字符相等(k為兩個字符長度最小值),返回值兩個字符串長度差值
String s1 = new String("abc");
String s2 = new String("ac");
String s3 = new String("abc");
String s4 = new String("abcdef");
System.out.println(s1.compareTo(s2)); // 不同輸出字符差值-1
System.out.println(s1.compareTo(s3)); // 相同輸出 0
System.out.println(s1.compareTo(s4)); // 前k個字符完全相同,輸出長度差值 -3
4. int compareToIgnoreCase(String str) 方法:與compareTo方式相同,但是忽略大小寫比較
String s1 = new String("abc");
String s2 = new String("ac");
String s3 = new String("ABc");
String s4 = new String("abcdef");
System.out.println(s1.compareToIgnoreCase(s2)); // 不同輸出字符差值-1
System.out.println(s1.compareToIgnoreCase(s3)); // 相同輸出 0
System.out.println(s1.compareToIgnoreCase(s4)); // 前k個字符完全相同,輸出長度差值 -3
2.3 字符串查找
字符串查找也是字符串中非常常見的操作,String類提供的常用查找的方法:
1.char charAt(int index)
返回index位置上字符,如果index為負數或者越界,拋出
IndexOutOfBoundsException異常
2.int indexOf(int ch) 返回ch第一次出現的位置,沒有返回-1
3.int indexOf(int ch, int fromIndex) 從fromIndex位置開始找ch第一次出現的位置,沒有返回-1
4.int indexOf(String str) 返回str第一次出現的位置,沒有返回-1
5.int indexOf(String str, intfromIndex)從fromIndex位置開始找str第一次出現的位置,沒有返回-1
6.int lastIndexOf(int ch) 從后往前找,返回ch第一次出現的位置,沒有返回-1
7.int lastIndexOf(int ch, int fromIndex)從fromIndex位置開始找,從后往前找ch第一次出現的位置,沒有返回-1
8. int lastIndexOf(String str) 從后往前找,返回str第一次出現的位置,沒有返回-1
9.int lastIndexOf(String str, int fromIndex) 從fromIndex位置開始找,從后往前找str第一次出現的位置,沒有返
注意:上述方法都是實例方法。
2.4 轉化
1.數值和字符串轉化
// 數字轉字符串
String s1 = String.valueOf(1234);
String s2 = String.valueOf(12.34);
String s3 = String.valueOf(true);
System.out.println(s1);
System.out.println(s2);
System.out.println(s3);
// 字符串轉數字
// 注意:Integer、Double等是Java中的包裝類型,這個后面會講到
int data1 = Integer.parseInt("1234");
double data2 = Double.parseDouble("12.34");
System.out.println(data1);
System.out.println(data2);
2.大小寫轉換
String s1 = "hello";
String s2 = "HELLO";
3.字符串轉數組
//字符串轉數組
String s1 = "acbd";
char[] arr = s1.toCharArray();for (int i = 0; i < arr.length ;i++) {System.out.println(arr[i]);}//數組轉字符串String s2 = new String(arr);System.out.println(s2);
4.格式化
String s = String.format("%d-%d-%d", 2019, 9,14);
System.out.println(s);
2.5 字符串替換
使用一個指定的新的字符串替換掉已有的字符串數據,可用的方法如下:
String str = "helloworld" ;System.out.println(str.replaceAll("l", "_"));System.out.println(str.replaceFirst("l", "_"));
注意事項: 由于字符串是不可變對象, 替換不修改當前字符串, 而是產生一個新的字符串.
2.6 字符串拆分
可以將一個完整的字符串按照指定的分隔符劃分為若干個子字符串。
可用方法如下:
方法? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?功能
String[] split(String regex)? ? ? ? ? ? ? ? ?將字符串全部拆分
String[] split(String regex, int limit)? ? 將字符串以指定的格式,拆分為limit組
代碼示例:實現字符串的拆分處理(全部)
String str = "hello world hello bit" ;String[] result = str.split("l") ; // 按照空格拆分for(String s: result) {
System.out.print(s);}
代碼示例:字符串的部分拆分
String str = "hello world hello bit" ;String[] result = str.split("ll",2) ;for(String s: result) {
System.out.println(s);
拆分是特別常用的操作. 一定要重點掌握. 另外有些特殊字符作為分割符可能無法正確切分, 需要加上轉義.
代碼示例:拆分IP地址
String str = "192.168.1.1" ;String[] result = str.split("\\.") ;for(String s: result) {
System.out.println(s);
注意事項:
1. 字符"|", "*","+"都得加上轉義字符,前面加上\\
2.而如果是"\",那么就得寫成"\\\\"
3.如果一個字符串中有多個分隔符,可以用"|"作為連字符
代碼示例:多次拆分
String str = "name=zhangsan&age=18" ;String[] result = str.split("&") ;for (int i = 0; i < result.length; i++) {
String[] temp = result[i].split("=") ;
System.out.println(temp[0]+" = "+temp[1]);}}
2.7 字符串截取
從一個完整的字符串之中截取出部分內容。可用方法如下:
方法? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? 功能
String substring(int beginIndex)? ? ? ? ? ? ? ? ? ? ? ?從指定索引截取到結尾
String substring(int beginIndex, int endIndex) 截取部分內容
String str = "helloworld" ;System.out.println(str.substring(5));System.out.println(str.substring(0, 5));
注意事項:
1. 索引從0開始
2. 注意前閉后開區間的寫法, substring(0, 5) 表示包含 0 號下標的字符, 不包含 5 號下標
2.8 其他操作方法
代碼示例:觀察trim()方法使用
String str = " hello world " ;System.out.println("["+str+"]");System.out.println("["+str.trim()+"]");
代碼示例:大小寫轉換
String str = " hello%$$%@#$%world 哈哈哈 " ;System.out.println(str.toUpperCase());System.out.println(str.toLowerCase());
這兩個函數只轉換字母
2.9? 字符串的不可變性
String是一種不可變對象. 字符串中的內容是不可改變。字符串不可被修改,是因為:
1. String類在設計時就是不可改變的,String類實現描述中已經說明了
以下來自JDK1.8中String類的部分實現:
1. String類被final修飾,表明該類不能被繼承
2. value被修飾被final修飾,表明value自身的值不能改變,即不能引用其它字符數組,但是其引用空間中
的內容可以修改。
2. 所有涉及到可能修改字符串內容的操作都是創建一個新對象,改變的是新對象
【糾正】網上有些人說:字符串不可變是因為其內部保存字符的數組被final修飾了,因此不能改變。
這種說法是錯誤的,不是因為String類自身,或者其內部value被final修飾而不能被修改。
final修飾類表明該類不想被繼承,final修飾引用類型表明該引用變量不能引用其他對象,但是其引用對象中的內容是可以修改的
為什么 String 要設計成不可變的?(不可變對象的好處是什么?)?
1. 方便實現字符串對象池. 如果 String 可變, 那么對象池就需要考慮寫時拷貝的問題了.
2. 不可變對象是線程安全的.
3. 不可變對象更方便緩存 hash code, 作為 key 時可以更高效的保存到 HashMap 中
那如果想要修改字符串中內容,該如何操作呢?
3.StringBuilder和StringBuffer
3.1 StringBuilder的介紹
由于String的不可更改特性,為了方便字符串的修改,Java中又提供StringBuilder和StringBuffer類。這兩個類大部分功能是相同的,這里介紹 StringBuilder常用的一些方法,其它需要用到了大家可參閱 StringBuilder在線文檔
方法? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?說明
StringBuff append(String str)? ? ? ? 在尾部追加,相當于String的+=,可以追加:boolean、char,char[]、double、float、int、long、Object、String、StringBuff的變量
char charAt(int index)? ? ? ? ? ? ? ? 獲取index位置的字符int length() 獲取字符串的長度
int capacity()? ? ? ? ? ? ? ? ? ? ? 獲取底層保存字符串空間總的大小
void ensureCapacity(int mininmumCapacity)? ? ? ? 擴容
void setCharAt(int index,char ch)? ? ? ? ? ? ? ? ? ? ?將index位置的字符設置為ch
int indexOf(String str)? ? ? ? ? ? ? 返回str第一次出現的位置
int indexOf(String str, int fromIndex)? ? ? ? 從fromIndex位置開始查找str第一次出現的位置
int lastIndexOf(String str)? ? ? ? ? 返回最后一次出現str的位置
int lastIndexOf(String str,int fromIndex)? ? ?從fromIndex位置開始找str最后一次出現的位置
StringBuff insert(int offset, String str)? ? ? ? ? 在offset位置插入:八種基類類型 & String類型 & Object類型數據
StringBuffer deleteCharAt(int index)? ? ? ? 刪除index位置字符
StringBuffer delete(int start, int end)? ? ? ? ?刪除[start, end)區間內的字符
StringBuffer replace(int start, int end, String str)? ? ? ? ?將[start, end)位置的字符替換為str
String substring(int start)? ? ? ? ? ? ? ? 從start開始一直到末尾的字符以String的方式返回
String substring(int start,int end)? ? ? 將[start, end)范圍內的字符以String的方式返回
StringBuffer reverse()? ? ? ? ? ? ? ? ? ?反轉字符串
String toString()? ? ? ? ? ? ? ? ?將所有字符按照String的方式返回
示例:
import java.util.Arrays;public class Test12 {public static void main(String[] args) {
StringBuilder sb1 = new StringBuilder("hello");
StringBuilder sb2 = sb1;
// 追加:即尾插-->字符、字符串、整形數字sb1.append(' '); // hello
sb1.append("world"); // hello world
sb1.append(123); // hello world123
System.out.println(sb1); // hello world123
System.out.println(sb1 == sb2); // true
System.out.println(sb1.charAt(0)); // 獲取0號位上的字符 h
System.out.println(sb1.length()); // 獲取字符串的有效長度14
System.out.println(sb1.capacity()); // 獲取底層數組的總大小
sb1.setCharAt(0, 'H'); // 設置任意位置的字符 Hello world123
sb1.insert(0, "Hello world!!!"); // Hello world!!!Hello world123
System.out.println(sb1);
System.out.println(sb1.indexOf("Hello")); // 獲取Hello第一次出現的位置
System.out.println(sb1.lastIndexOf("hello")); // 獲取hello最后一次出現的位置
sb1.deleteCharAt(0); // 刪除首字符
sb1.delete(0,5); // 刪除[0, 5)范圍內的字符
String str = sb1.substring(0, 5); // 截取[0, 5)區間中的字符以String的方式返回
System.out.println(str);
sb1.reverse(); // 字符串逆轉
str = sb1.toString(); // 將StringBuffer以String的方式返回
System.out.println(str);
}}
從上述例子可以看出:String和StringBuilder最大的區別在于String的內容無法修改,而StringBuilder的內容可以修改。頻繁修改字符串的情況考慮使用StringBuilder。
注意:String和StringBuilder類不能直接轉換。如果要想互相轉換,可以采用如下原則:
String變為StringBuilder: 利用StringBuilder的構造方法或append()方法
StringBuilder變為String: 調用toString()方法。
String、StringBuffer、StringBuilder的區別
相同點:底層都是通過char數組實現的
不同點:
String對象一旦創建,其值是不能修改的,如果要修改,會重新開辟內存空間來存儲修改之后的對象;而StringBuffer和StringBuilder對象的值是可以被修改的;
StringBuffer幾乎所有的方法都使用synchronized實現了同步,線程比較安全,在多線程系統中可以保證數據同步,但是效率比較低;而StringBuilder 沒有實現同步,線程不安全,在多線程系統中不能使用 StringBuilder,但是效率比較高。
如果我們在實際開發過程中需要對字符串進行頻繁的修改,不要使用String,否則會造成內存空間的浪費;當需要考慮線程安全的場景下使用 StringBuffer,如果不需要考慮線程安全,追求效率的場景下可以使用 StringBuilder。
?