常用類:String相關類與方法
尚硅谷JavaSE筆記-20
String類
String類特性
- 表示字符串,內容以
""
包裹表示,稱為字面量的定義方式 - 是一個
final
類,不可被繼承 - 實現了
Serializable
接口,可以序列化 - 實現了
Comparable
接口,表示可以比較大小 - 內部定義了一個
final char[] value
數組來實際儲存數據,代表不可變的字符序列- 當我們用字面量(區別於
new
)定義了一個String str1="abc"
,相當於在方法區的字符串常量池中新增了一個"abc"
序列,其為不可變的 - 字符串常量池中不會儲存內容相同的字符串,比如我再新增一個
str2="abc"
,則他們指向同一地址。- 延伸
str6="a"+"bc"
,在聲明時字面量相加,等於常量跟常量拼接,都是在常量池,所以指向同樣位置
- 延伸
- 而當我把
str1="hello"
,或是用+
拼接了其他內容,或是用replace
取代了其中某一位的字,都是在常量池重新開闢空間,所謂不可變是這個意思
- 當我們用字面量(區別於
- 如果是用
String str3=new String("abc");
方法生成,則是創在堆中,str3本身是指向堆的地址,去比==
都是false
。這個動作實際開闢了2個記憶體空間,它在堆中的value才指向常量池- 如果是
new
一個構造器生成時賦予的name傳入形參則跟new
String是不同的,傳入形參也是類似於字面量的方法(因為顯然多在堆中開闢位置是毫無必要的) - 若是用+拼接有任何涉及到變量的,例如
str4=str1+"def"
,都是相當於在堆空間new
的操作,==
去比較時全都false
- 但如果變量被
final
修飾(正常不會有人這麼做),相當於這個變量存在常量池,又會是常量跟常量拼接,所以指向同樣位置 - 若是用
intern()
方法,例如str5=str1.intern();
,返回值是在常量池中,==
比較是true
- 如果是
面試題-判斷結果
public class StringTest {
String str = new String("good");
char[] ch = {'t', 'e', 's', 't'};
public void change(String str, char[] ch) {
str = "bad";
ch[0] = 'b';
}
public static void main(String[] args) {
StringTest ex = new StringTest();
ex.change(ex.str, ex.ch);
System.out.println(ex.str); // good
System.out.println(ex.ch); // best
}
}
-
切記傳形參時,引用類型傳的是地址值
-
這個案例中形參只是正好也叫
str
而已,仔細看編譯器提示它應該會是灰色的,因為根本沒用到。 -
change的
str
是方法的形參,當屬性的str
作為實參傳入時,把屬性str
的地址給了形參 -
形參
str
指向方法區造了一個"bad"
,但也沒用到然後隨著方法結束就消亡了。而屬性str
仍然指向"good"
-
ch的狀況同理,它的地址被傳進去給了形參,形參跟實參都是指向同一個位置,地址指向的首位元素被改成了b,所以改變
-
而若使用
this.str = "bad";
,形參叫啥名字、或有無這個string
的形參根本都無所謂,是等同於在其他地方str = "bad";
,在方法區常量池中重新開闢並重新賦值
String常用方法
int length()
:返回字數(一個中英文字母都是1)char charAt(index)
:返回指定下標,小心越位boolean isEmpty()
:返回是否空字串boolean equals(Object obj)
:比較內容是否相同boolean equalsIgnoreCase(String str)
:忽略大小寫比較內容是否相同String concat(String str2)
:將str2拼接到後面,等同於"+
"int CompareTo(String str2)
:根據字符串編碼,拿調用者減去str2
,可以用在排序String toLowerCase()
:返回一份全轉為小寫的副本String toUpperCase()
:返回一份全轉為大寫的副本String trim()
:刪除前後的空格(中間的不算)String substring(int 開始,結尾)
:根據下標取切片,左閉右開(含頭不含尾),結尾可省略- 即是說想從頭複製到尾形參是
(0,length)
,注意超過會溢出
- 即是說想從頭複製到尾形參是
boolean endsWith(String str)
:返回是否以str
結尾,區分大小寫,長度不限boolean startsWith(String str)
:返回是否以str
開頭,區分大小寫,長度不限boolean startsWith(String str,int 下標)
:從指定下標開始(含)算
boolean contains(String str)
:返回是否包含一個以上str
,區分大小寫,長度不限int indexOf(String str)
:返回第一次匹配到str
的下標,區分大小寫,長度不限。-1表示無int indexOf(String str,int 下標)
:從指定下標開始(含)算
int lastIndexOf(String str)
:返回最後一次匹配到str
的下標,區分大小寫,長度不限。-1表示無int lastIndexOf(String str,int 下標)
:從指定下標開始(含)反向搜索(往左搜)當
indexOf與
lastIndexOf`返回相同,要嘛只存在一個解,要嘛都-1
String replace(string 舊,新)
:拿舊換新,區分大小寫,長度不限,預設全部取代String replaceAll(string 正則,新)
:正則式替換,全部取代String replaceFirst(string 正則,新)
:正則式替換,只取代第一個匹配boolean match(string 正則)
:匹配是否符合正則String[] split(string 正則)
:依照正則切分成字符串的數組String[] split(string 正則,int limit)
:最多切成幾個元素,超過全放在最後
String類、基本數據轉換(複習)
String
轉成基本數據、包裝類:Integer.parseInt(str)
- 基本數據、包裝類轉String:
String.valueOf(num)
- 或是直接
str2=num+"";
// 是在堆裡
- 或是直接
String
轉成char[]:toCharArray()
- 反轉,直接調用構造器
= new String(arr)
- 反轉,直接調用構造器
String
轉成byte[]:getBytes(),會返回位元組(字節),預設編碼utf-8
(中文字大多是3位),utf-8本身是一個可變長度的編碼方式getBytes(字元編碼)
:也可以指定編碼- 反轉,一樣調用構造器
= new String(arr)
,形參也可以加,“指定編碼”
StringBuffer
面試重點題 | 字符序列 | 特性 |
---|---|---|
String | 不可變 | 改變等於新創,效率最低 |
StringBuffer | 可變 | 線程安全、效率低 |
StringBuilder | 可變 | 線程不安全、效率高 |
以下沒特別講都是
StringBuffer
跟StringBuilder
都共有的特性
- 底層都是
char[]
,但不同於String
有final
修飾,StringBuffer
的內容是可變的 new StringBuffer
或StringBuilder
時,capacity
預設容量是16,注意容量不等於當前長度length
- 當使用
append
拼接其他內容時,會先判斷是否超過容量,超過就創立一個新的數組容量是原有*2再+2,把原來的複製過去再拼接 - 超過太多會考慮直接計算欲拼接完的字符長度來當新容量
- 實際開發中,由於拼接複製效率低,最好在構造時就預想好需使用的容量,
StringBuffer(int capacity)
、StringBuilder(int capacity)
StringBuffer常用方法
範圍沒特別說都是左閉右開(含頭不含尾),預設返回都是對
this
操作都是
StringBuffer
跟StringBuilder
共有的方法
append(xxx)
:拼接,內容xxx都視為String拚上去delete(int start,int end)
:刪除指定範圍的內容replace(首,尾,str)
:取代指定範圍的內容為strsetCharAt(index,ch)
:修改指定位內容為chinsert(index,str)
:從指定位置插入str,插完的index位即是str開頭reverse()
:序列反轉indexOf(String str)
:返回第一次匹配到str的下標,區分大小寫,長度不限。-1表示無
String算法題練習
public class CharCount {
public static void main(String[] args) {
// 获取一个字符串在另一个字符串中出现的次数。
//比如:获取" ab"在"abkkcadkabkebfkabkskab" 中出现的次数
String s0 = "abkkcadkabkebfkabkskab";
// String s0 = "abkkcadkabkebfkabababababababababababababerg654erg654erg564abababkskab";
String st = "ab";
int count = 0;
if (s0.contains(st)) {
String s1 = s0.replace(st, "@");
char[] c1 = s1.toCharArray();
System.out.println(Arrays.toString(c1));
for (int i = 0; i < c1.length; i++) {
if (c1[i] == 64) {
count++;
}
}
} else {
count = 0;
}
System.out.println("次數=" + count);
}
/*
老師思路:
while包住,用indexOf(子str,index)
匹配有,就count++,index+=子str長度
*/
}
public class Reverse {
public static void main(String[] args) {
// 将一个字符串进行反转。将字符串中指定部分进行反转。比如"abcdefg"反转为"abfedcg"
int left = 2; // 擷取左邊保留位數
int right = 1; // 擷取右邊倒數保留位數
String str = "abcdefghijk";
String s2 = str.substring(left, str.length() - right);
System.out.println("欲反轉順序區=" + s2);
char[] rs2 = s2.toCharArray();
for (int i = 0; i < rs2.length / 2; i++) {
char c = rs2[i];
rs2[i] = rs2[rs2.length - 1 - i];
rs2[rs2.length - 1 - i] = c;
}
System.out.println("反轉後=" + Arrays.toString(rs2));
String rrs2 = new String(rs2);
String str3 = str.substring(0, left) + rrs2 + str.substring(str.length() - right);
System.out.println("最終=" + str3);
}
}
/*
老師的思路一:
不切出sub數組,而是在for循環直接換位
for(int x=左,y=右;x<y;x++,y--){
char temp=arr[x];
arr[x]=arr[y]
...
思路二:
切出來,倒著遍歷數組,拼成一個反著的string,再接回去
思路三:
切出來,用stringBuilder裝,反轉
*/
public class Contain {
public static void main(String[] args) {
// 4.获取两个字符串中最大相同子串。比如:
//str1 = "abcwerthelloyuiodef";str2 = "cvhellobnm"
//前提,只存在一解
//提示:将短的那个串进行长度依次递减的子串与较长的串比较。
String str1 = "abcwerthelloyuiodef";
String str2 = "cvhellobnm";
String ans = "";
for (int i = 0; i < str2.length(); i++) {
String str3 = str2.substring(i);
if (str1.contains(str3)) {
ans = str3;
break;
} else {
String str4 = str2.substring(i, str2.length() - 1 - i);
if (str1.contains(str4)) {
ans = str4;
break;
}
}
}
System.out.println("ans=" + ans);
}
/*
老師思路:
用兩個循環包住,外部控制位數從長度--,內部一個個去切,直到contains符合
*/
}
小結
String
類的不可變性:底層是一個final char[]
,長度跟內容自然不可變,改變都是新造一個重給地址
上次修改於 2021-12-05