Java常用API(五)
9.6 字符串
java.lang.String 類代表字符串。Java語言提供對象字符串的特殊支持:
- Java程序中所有的字符串文字(例如
"abc")都可以被看作是實現此類的實例,即所有""引起來的内容都是字符串對象。 - Java語言提供對字符串串聯符号(
+),即Java重載了+的功能。 - Java語言提供了将任意類型對象轉換爲字符串的特殊支持(
toString()方法)。
9.6.1 字符串的特點
- 字符串
String類型本身是final聲明的,意味着我們不能繼承String。 String對象内部是用字符數組進行保存的JDK8的
String内部是用字符數組進行保存的private final char[] value;"abc"等效于char[] data={ 'a', 'b', 'c' }。例如: String str = "abc"; 相當于: char data[] = {'a', 'b', 'c'}; String str = new String(data); // String底層是靠字符數組實現的。JDK9之後
String内部是用byte數組進行保存的private final byte[] value; private final byte coder;//新增加的字段,表示字符串采用的編碼 1是UTF-16BE,0是LATIN1定義的字符串中,如果沒有漢字,每個字符将采用
LATIN1編碼表存儲,如果字符串中包含漢字等非ASCII碼表的字符,存儲的編碼就是UTF-16BE,相比JDK1.8節約内存。
- 字符串的對象也是不可變對象,意味着一旦進行修改,就會産生新對象。
爲什麽
String對象不可變?或者說String類是如何設計爲對象不可變的?(面試題)String類中value數組是final修飾的,意味着這個數組地址不可變,即不能對同一個String對象的value數組進行擴容、縮容等。- 雖然
final修飾的value數組元素值可以修改。但是由于它是private修飾,外部不能直接操作它,所以在String類外無法直接修改value數組的元素值(除非用反射)。 String類型中提供的所有方法實現涉及到value數組長度需要變化,或value元素值需要變化,都是用新對象來表示修改後内容的。
綜上3點保證了
String對象的不可變。- 既然
String對象不可變,那麽我們“修改”String對象要如何操作?- 我們修改了字符串後,如果想要獲得新的内容,必須重新接收。
- 如果程序中涉及到大量的字符串的修改操作,那麽此時的時空消耗比較高。可能需要考慮使用
StringBuilder或StringBuffer的可變字符序列。
就因爲字符串對象設計爲不可變,所以可以共享。Java中把需要共享的字符串常量對象放在字符串常量池中。
String s1 = "abc"; String s2 = "abc"; System.out.println(s1 == s2); // 内存中隻有一個"abc"對象被創建,同時被s1和s2共享。
9.6.2 API方法
1、構造字符串對象
除了直接 "" 構建字符串之外,還可以通過以下4個方式:構造器、valueOf 方法、+、toString 方法
使用構造方法
public String():初始化新創建的String對象,以使其表示空字符序列。String(String original): 初始化一個新創建的String對象,使其表示一個與參數相同的字符序列;換句話說,新創建的字符串是該參數字符串的副本。public String(char[] value):通過當前參數中的字符數組來構造新的String。public String(char[] value,int offset, int count):通過字符數組的一部分來構造新的String。public String(byte[] bytes):通過使用平台的默認字符集解碼當前參數中的字節數組來構造新的String。public String(byte[] bytes,String charsetName):通過使用指定的字符集解碼當前參數中的字節數組來構造新的String。
構造舉例,代碼如下:
//字符串常量對象 String str = "hello"; // 無參構造 String str1 = new String(); //創建"hello"字符串常量的副本 String str2 = new String("hello"); //通過字符數組構造 char chars[] = {'a', 'b', 'c','d','e'}; String str3 = new String(chars); String str4 = new String(chars,0,3); // 通過字節數組構造 byte bytes[] = {97, 98, 99 }; String str5 = new String(bytes); String str6 = new String(bytes,"GBK");使用靜态方法
valueOf和copyValueOfstatic String copyValueOf(char[] data): 返回指定數組中表示該字符序列的Stringstatic String copyValueOf(char[] data, int offset, int count):返回指定數組中表示該字符序列的Stringstatic String valueOf(char[] data): 返回指定數組中表示該字符序列的Stringstatic String valueOf(char[] data, int offset, int count): 返回指定數組中表示該字符序列的Stringstatic String valueOf(xx value):xx支持各種數據類型,返回各種數據類型的value參數的字符串表示形式。
public static void main(String[] args) { char[] data = {'h','e','l','l','o','j','a','v','a'}; String s1 = String.copyValueOf(data); String s2 = String.copyValueOf(data,0,5); int num = 123456; String s3 = String.valueOf(num); System.out.println(s1); System.out.println(s2); System.out.println(s3); }任意類型與字符串
+任意數據類型與
"字符串"進行拼接,結果都是字符串類型public static void main(String[] args) { int num = 123456; String s = num + ""; System.out.println(s); Student stu = new Student(); String s2 = stu + "";//自動調用對象的toString(),然後與""進行拼接 System.out.println(s2); }任意對象.
toStringObject類中聲明了
toString()方法,因此任意對象都可以調用toString方法,轉爲字符串類型。如果沒有重寫toString的話,返回的默認是“對象的運行時類型@對象的hashCode值的十六進制值”public static void main(String[] args) { LocalDate today = LocalDate.now(); String str = today.toString(); System.out.println(str); }
2、求字符串的長度
int length():返回字符串的長度@Test public void test1(){ System.out.println("hello".length()); }
3、轉大小寫
String toLowerCase():将字符串中大寫字母轉爲小寫String toUpperCase():将字符串中小寫字母轉爲大寫
@Test
public void test2(){
System.out.println("Hello".toLowerCase());
System.out.println("Hello".toUpperCase());
}
4、字符串對象的比較
涉及到以下5個方式方法:==、equals、equalsIgnoreCase、compareTo、compareToIgnoreCase、Collator
====運算符:比較是兩個字符串對象的地址@Test public void test1(){ String str1 = "hello"; String str2 = "hello"; System.out.println(str1 == str2);//true,說明str1和str2指向同一個字符串對象 String str3 = new String("hello"); String str4 = new String("hello"); System.out.println(str1 == str4); //false System.out.println(str3 == str4); //false }equals方法比較boolean equals(Object obj)方法:比較是兩個字符串對象的内容,因爲String類型重寫equals,equals方法比較字符串内容時嚴格區分大小寫。@Test public void test2(){ String str1 = "hello"; String str2 = "hello"; System.out.println(str1.equals(str2));//true String str3 = new String("hello"); String str4 = new String("hello"); System.out.println(str1.equals(str3));//true System.out.println(str3.equals(str4));//true }equalsIgnoreCase方法boolean equalsIgnoreCase(String str)方法:比較是兩個字符串對象的内容,并且不區分大小寫。@Test public void test3(){ String str1 = new String("hello"); String str2 = new String("HELLO"); System.out.println(str1.equalsIgnoreCase(str2)); //true } @Test public void test04(){ //随機生成驗證碼,驗證碼由0-9,A-Z,a-z的4位字符組成 char[] array = new char[26*2+10]; for (int i = 0; i < 10; i++) { array[i] = (char)('0' + i); } for (int i = 10,j=0; i < 10+26; i++,j++) { array[i] = (char)('A' + j); } for (int i = 10+26,j=0; i < array.length; i++,j++) { array[i] = (char)('a' + j); } char[] code = new char[4]; Random rand = new Random(); for (int i = 0; i < 4; i++) { code[i]= array[rand.nextInt(array.length)]; } String codeString = new String(code); System.out.println("驗證碼:" + codeString); //将用戶輸入的單詞全部轉爲小寫,如果用戶沒有輸入單詞,重新輸入 Scanner input = new Scanner(System.in); System.out.print("請輸入驗證碼:"); String inputCode = input.nextLine(); if(!codeString.equalsIgnoreCase(inputCode)){ System.out.println("驗證碼輸入不正确"); }else{ System.out.println("驗證碼輸入正确"); } input.close(); }compareTo方法int compareTo(String str)方法:String類型實現了java.lang.Comparable接口,重寫了Comparable接口的抽象方法,即String對象支持自然排序,該方法按照字符的Unicode編碼值進行比較大小的,嚴格區分大小寫。@Test public void test5(){ String str1 = "hello"; String str2 = "world"; String str3 = "HELLO"; System.out.println(str1.compareTo(str2));//-15 System.out.println(str1.compareTo(str3));//32 } @Test public void test6(){ String[] arr = {"hello","java","chai","Jack","hi"}; System.out.println(Arrays.toString(arr)); Arrays.sort(arr); System.out.println(Arrays.toString(arr)); }compareToIgnoreCase方法int compareToIgnoreCase(String str):String類型支持不區分大小寫比較字符串大小。具體原理是先統一大小寫再比較大小。@Test public void test7(){ String str1 = "hello"; String str2 = "world"; String str3 = "HELLO"; System.out.println(str1.compareToIgnoreCase(str2));//-15 System.out.println(str1.compareToIgnoreCase(str3));//0 } @Test public void test8(){ String[] arr = {"hello","java","chai","Jack","Hi"}; System.out.println(Arrays.toString(arr)); Arrays.sort(arr, new Comparator() { @Override public int compare(Object o1, Object o2) { String s1 = (String) o1; String s2 = (String) o2; return s1.compareToIgnoreCase(s2); } }); System.out.println(Arrays.toString(arr)); }區分語言環境的
String比較java.text.Collator類執行區分語言環境的String比較。@Test public void test9(){ String[] arr = {"張三","李四","柴林燕","王五","尚矽谷"}; System.out.println(Arrays.toString(arr)); Arrays.sort(arr, new Comparator() { @Override public int compare(Object o1 , Object o2) { String s1 = (String) o1; String s2 = (String) o2; Collator collator = Collator.getInstance(Locale.CHINA); return collator.compare(s1,s2); } }); System.out.println(Arrays.toString(arr)); }
5、去掉前後空白
String trim():去掉字符串前後空白符@Test public void test3(){ System.out.println("[" + " hello world ".trim() + "]"); } @Test public void test04(){ //将用戶輸入的單詞轉爲小寫,如果用戶沒有輸入單詞,重新輸入 Scanner input = new Scanner(System.in); String word; while(true){ System.out.print("請輸入單詞:"); word = input.nextLine(); if(word.trim().length()!=0){ word = word.toLowerCase(); break; } } System.out.println("你輸入的單詞是:" + word); input.close(); }
6、空字符串的判斷
isEmpty()isBlank()@Test public void test3()throws Exception{ String s = ""; System.out.println(s.isEmpty()); System.out.println(s.length()==0); System.out.println(s.equals("")); System.out.println("".equals("")); System.out.println(s.isBlank()); System.out.println(s.trim().length()==0); }
7、字符串開頭與結尾的判斷
boolean startsWith(xx):是否以xx開頭@Test public void test4(){ String name = "張三"; System.out.println(name.startsWith("張")); }boolean endsWith(xx):是否以xx結尾@Test public void test5(){ String file = "Hello.txt"; if(file.endsWith(".java")){ System.out.println("Java源文件"); }else if(file.endsWith(".class")){ System.out.println("Java字節碼文件"); }else{ System.out.println("其他文件"); } }
8、字符串内容查找
boolean contains(xx):是否包含xxint indexOf(xx):從前往後找當前字符串中xx,即如果有返回第一次出現的下标,要是沒有返回-1int lastIndexOf(xx):從後往前找當前字符串中xx,即如果有返回最後一次出現的下标,要是沒有返回-1@Test public void test05(){ String str = "尚矽谷是一家靠譜的培訓機構,尚矽谷可以說是IT培訓的小清華,JavaEE是尚矽谷的當家學科,尚矽谷的大數據培訓是行業獨角獸。尚矽谷的前端和運維專業一樣獨領風騷。"; System.out.println("是否包含清華:" + str.contains("清華")); System.out.println("培訓出現的第一次下标:" + str.indexOf("培訓")); System.out.println("培訓出現的最後一次下标:" + str.lastIndexOf("培訓")); }
9、字符串截取
String substring(int beginIndex):返回一個新的字符串,它是此字符串的從beginIndex開始截取到最後的一個子字符串。String substring(int beginIndex, int endIndex):返回一個新字符串,它是此字符串從beginIndex開始截取到endIndex(不包含)的一個子字符串。@Test public void test06(){ String str = "helloworldjavaatguigu"; String sub1 = str.substring(5); String sub2 = str.substring(5,10); System.out.println(sub1); System.out.println(sub2); } @Test public void test07(){ String fileName = "快速學習Java的秘訣.dat"; //截取文件名 System.out.println("文件名:" + fileName.substring(0,fileName.lastIndexOf("."))); //截取後綴名 System.out.println("後綴名:" + fileName.substring(fileName.lastIndexOf("."))); }
10、獲取char和char[]
char charAt(index):返回[index]位置的字符char[] toCharArray(): 将此字符串轉換爲一個新的字符數組返回将字符數組轉爲
String對象,可以使用之前介紹過的構造器和靜态方法valueOf或copyValueOf等String(char[] value):返回指定數組中表示該字符序列的String。String(char[] value, int offset, int count):返回指定數組中表示該字符序列的String。static String copyValueOf(char[] data): 返回指定數組中表示該字符序列的Stringstatic String copyValueOf(char[] data, int offset, int count):返回指定數組中表示該字符序列的Stringstatic String valueOf(char[] data, int offset, int count): 返回指定數組中表示該字符序列的Stringstatic String valueOf(char[] data):返回指定數組中表示該字符序列的String
@Test public void test08(){ //将字符串中的字符按照大小順序排列 String str = "helloworldjavaatguigu"; char[] array = str.toCharArray(); Arrays.sort(array); str = new String(array); System.out.println(str); } @Test public void test09(){ //将首字母轉爲大寫 String str = "jack"; str = Character.toUpperCase(str.charAt(0))+str.substring(1); System.out.println(str); }
11、字符串的編碼與解碼
byte[] getBytes():編碼,把字符串變爲字節數組,按照平台默認的字符編碼方式進行編碼byte[] getBytes(字符編碼方式):按照指定的編碼方式進行編碼new String(byte[] )或new String(byte[], int, int):解碼,按照平台默認的字符編碼進行解碼new String(byte[],字符編碼方式 )或new String(byte[], int, int,字符編碼方式):解碼,按照指定的編碼方式進行解碼==(編碼方式見附錄10.7.1)==

@Test public void test10()throws Exception{ byte[] data = {(byte)0B01100001, (byte)0B01100010, (byte)0B01100011, (byte)0B01100100}; System.out.println(new String(data,"ISO8859-1")); System.out.println(new String(data,"GBK")); System.out.println(new String(data,"UTF-8")); } @Test public void test11()throws Exception{ byte[] data = {(byte)0B01100001, (byte)0B01100010, (byte)0B01100011,(byte)0B11001001,(byte)0B11010000}; System.out.println(new String(data,"ISO8859-1")); System.out.println(new String(data,"GBK")); System.out.println(new String(data,"UTF-8")); } @Test public void test12()throws Exception{ byte[] data = {(byte)0B01100001,(byte)0B11100101, (byte)0B10110000, (byte)0B10011010, (byte)0B11000111, (byte)0B10101011}; System.out.println(new String(data,"ISO8859-1")); System.out.println(new String(data,"GBK")); System.out.println(new String(data,"UTF-8")); } @Test public void test13() throws Exception { String str = "中國"; System.out.println(str.getBytes("ISO8859-1").length);// 2 // ISO8859-1把所有的字符都當做一個byte處理,處理不了多個字節 System.out.println(str.getBytes("GBK").length);// 4 每一個中文都是對應2個字節 System.out.println(str.getBytes("UTF-8").length);// 6 常規的中文都是3個字節 /* * 不亂碼:(1)保證編碼與解碼的字符集名稱一樣(2)不缺字節 */ System.out.println(new String(str.getBytes("ISO8859-1"), "ISO8859-1"));// 亂碼 System.out.println(new String(str.getBytes("GBK"), "GBK"));// 中國 System.out.println(new String(str.getBytes("UTF-8"), "UTF-8"));// 中國 }
12、字符串匹配正則表達式
boolean matches(正則表達式):判斷當前字符串是否匹配某個正則表達式。==(正則表達式詳細見附錄)==@Test public void test16(){ //簡單判斷是否全部是數字,這個數字可以是1~n位 String str = "12a345"; //正則不是Java的語法,它是獨立與Java的規則 //在正則中\是表示轉義, //同時在Java中\也是轉義 boolean flag = str.matches("\\d+"); System.out.println(flag); } @Test public void test17(){ String str = "123456789"; //判斷它是否全部由數字組成,并且第1位不能是0,長度爲9位 //第一位不能是0,那麽數字[1-9] //接下來8位的數字,那麽[0-9]{8}+ boolean flag = str.matches("[1-9][0-9]{8}+"); System.out.println(flag); } @Test public void test18(){ //密碼要求:必須有大寫字母,小寫字母,數字組成,6位 System.out.println("Cly892".matches("^(?=.*[A-Z])(?=.*[a-z])(?=.*[0-9])[A-Za-z0-9]{6}$"));//true System.out.println("1A2c45".matches("^(?=.*[A-Z])(?=.*[a-z])(?=.*[0-9])[A-Za-z0-9]{6}$"));//true System.out.println("Clyyyy".matches("^(?=.*[A-Z])(?=.*[0-9])[A-Za-z0-9]{6}$"));//false /* (1)密碼的長度爲6,且隻能有[A-Za-z0-9]組成。 (2)另外,三個非捕獲組都能匹配到自己的值。 (?=.*[A-Z]):匹配值 C (?=.*[a-z]):匹配值 Clyya (?=.*[0-9]):匹配值 Clyya1 三個非捕獲組都有值,即都匹配上了就行。 非捕獲組是隻匹配不捕獲。 */ }
13、字符串内容的替換
String replace(xx,xx):不支持正則String replaceFirst(正則,value):替換第一個匹配部分String replaceAll(正則, value):替換所有匹配部分@Test public void test19(){ String str = "hello244world.java;887"; String s1 = str.replace("244",""); System.out.println("s1 = " + s1); String s2 = str.replaceFirst("\\d+",""); System.out.println("s2 = " + s2); //把其中的非字母去掉 String s3 = str.replaceAll("[^a-zA-Z]", ""); System.out.println("s3 = " + s3); }
14、字符串拆分
String[] split(正則):按照某種規則進行拆分@Test public void test20(){ String str = "Hello World java atguigu"; String[] all = str.split(" "); for (int i = 0; i < all.length; i++) { System.out.println(all[i]); } } @Test public void test21(){ String str = "1Hello2World3java4atguigu"; str = str.replaceFirst("\\d", ""); System.out.println(str); String[] all = str.split("\\d"); for (int i = 0; i < all.length; i++) { System.out.println(all[i]); } } @Test public void test23(){ String str = "張三.23|李四.24|王五.25"; //|在正則中是有特殊意義,我這裏要把它當做普通的| String[] all = str.split("\\|"); //轉成一個一個學生對象 Student[] students = new Student[all.length]; for (int i = 0; i < students.length; i++) { //.在正則中是特殊意義,我這裏想要表示普通的. String[] strings = all[i].split("\\.");//張三, 23 String name = strings[0]; int age = Integer.parseInt(strings[1]); students[i] = new Student(name,age); } for (int i = 0; i < students.length; i++) { System.out.println(students[i]); } }class Student { private String name; private int age; public Student(String name, int age) { this.name = name; this.age = age; } @Override public String toString() { return "Student{" + "name='" + name + '\'' + ", age=" + age + '}'; } }
15、字符串拼接方法
兩種方式: 字符串1.concat(字符串2):隻要拼接的不是空字符串,每次都new一個String 字符串1 + 字符串2: ""常量拼接,編譯器直接優化爲拼接後的字符串常量值 非""常量拼接,編譯器優化爲StringBuilder的append,然後再把結果toString。
package com.atguigu.string;
import org.junit.Test;
public class TestStringConcat {
@Test
public void test1(){
String s1 = "hello";
String s2 = "world";
String s3 = s1 + s2;
String s4 = s1.concat(s2);
System.out.println("s3 = " + s3);//helloworld
System.out.println("s4 = " + s4);//helloworld
}
}
class Test{
public void test1() {
String s1 = "hello" + "world";
}
public void test2() {
String s1 = "hello";
String s2 = s1 + "world";
}
public void test3() {
String s1 = "hello";
String s2 = "world";
String s3 = s1 + s2;
}
}


JDK1.9及以後的版本看到的是

動态指令invokedynamic指令會調用makeConcatWithConstants方法進行字符串的連接。該方法位于java.lang.invoke.StringConcatFactory類中,如果繼續往下跟蹤,最終還是調用StringBuilder類append方法,篇幅有限,就不一一展示了:



