前言:在我們學習String這個專題之前,讓我們回憶一下,在我們之前學習的C語言中有沒有String型別,答案是沒有的,在C++和java中都引進了字串型別,這樣讓我們在日常處理字串時,增添了諸多便利,
前期文章:
[java篇]包,繼承,組合
[java篇]多型,抽象類,介面
[java篇]圖書管理系統,是你的期末大作業嗎?
廢話不多說,開干!!!
文章目錄
- 1.認識String類
- 創建字串
- 字串常量池
- 案例分析:
- 案例一:
- 案例二:
- 案例三:
- 案例四:
- 案例五:
- 案例六:
- 案例七:
- 案例八:
- 案例九:
- 案例十:
- 正確理解==和equal的區別
- 理解字串不可變:
- 2.字符,位元組,字串
- 字符和字串
- 位元組與字串:
- 分析在什么的情況下使用字符陣列,在那種場合使用位元組陣列
- 3.字串常見操作:
- 字串比較:
- 字串拆分:
- 字串替換:
- 字串查找:
- 字串截取:
- 其他操作方法:
- 4.String,StringBuilder和StringBuffer之間的區別:
1.認識String類
讓我們先看看String類到底是個啥?
在我們學習java語法的時候,我們不可能把所有的java語法都記得非常清楚,所以我們就提供了一個專門查閱有關java學習的API檔案,

1.String型別,在java.lang包下,在我們使用String型別的時候,不用手動導包,系統會自行導包,
2.String型別被final修飾,表示這個型別不可更改,這個類不能被繼承,String類屬于一個密封類,
3.String型別繼承了Object型別
4.String型別實作了Comparator介面(在前期文章介紹過),CharSxequence介面,Serializable介面,
當我們要學習一個型別的時候,首先要了解它的構造方法,這樣我們在之后的學習中才會如魚得水,

我了個去,這這這,怎么這么多呢?這要學到什么時候,
其實我們常用的一共就是用兩種構造方法,上面的構造方法我們知道即可,
1.
String str = "hello world";
2.String str1 = new String("hello world");
創建字串
我們在創建字串的時候一般有兩種方法:
直接賦值,即String str = “hello world”;
另一種是利用構造方法:String str = new String(“abc”);在括號里面的字串,被存盤到new出的物件里面的value陣列 ,
字串常量池
字串常量池是利用哈希表實作的,所以在常量池中不允許出現相同的字串,String型別,采用共享模式,如果兩個變數所指向的字串相等,那么在字串常量池中只會保留一份字串,讓兩個變數同時指向這個字串,
其實在jvm中沒有劃分這個區域的,
來分析一下代碼:
public static void main(String[] args) {
String str = "123";
String str1 = "123";
System.out.println(str == str1);
}
這個應該很好判斷吧,但是你是不是按這個思路想的呢?
請看記憶體圖:

他們比較的是兩個參考是否相等,str和str1的字串內容都是hello 所以只能在字串常量池中存盤一份"hello",而String型別又具有共享模式,所以兩個參考同時都指向一塊記憶體空間,
構造方法下字串在堆中的存盤
String str = new String(abc);
str作為一個參考,它參考了一個物件,這個物件里面有一個value陣列,這個陣列指向的就是添加進來的字串,
利用構造方法賦值的缺點:
在堆中必須要開辟兩塊記憶體空間,并且在有一份會成為垃圾空間,
同一個字串可能被存放多次,浪費空間,
請簡述String型別兩種賦值的區別:
第一種:直接賦值:在堆中開辟了一塊記憶體空間,并且傳入的字串會自動的保存在字串常量池中,
第二種:利用構造方法進行賦值,在堆中會開辟兩塊記憶體空間,并且傳入的字串不會入池,必須通過intern()方法,手動入池,
案例分析:
請大家閱讀以下代碼,并回答true or false
案例一:
public static void main1(String[] args) {
String str1 = "hello world";
String str2 = new String("hello world");
System.out.println(str1 == str2);//false
}
大家先根據題目做出解答哦,以下會具體分析
單眼一看,這有什么難得,不就是比較吧,這不兩個字串相等,不就是true嘛,
哈哈哈,上當了吧,答案是false,其實它們比較的是是兩個字串的參考,這個參考就相當于與我們在C語言階段學習的指標一樣,當時java里面沒有指標哦,這個參考也沒有C/C++里面的指標那么的靈活,
str1作為一個參考,在堆疊幀里面開辟空間,而賦予str的值,是在堆上存放的
str2作為一個參考,它參考了一個String型別的物件,而這個參考在堆疊上開辟空間,物件被存放在堆上,
看圖說話:
分析:
- str1直接賦值,把"hello world"這個字串傳給了str1,在堆中,凡是被雙引號引起來的常量,都會存放在字串常量池中,
- str2是一個參考,他指向了一個物件,在物件里邊存在一個value陣列,這個陣列又參考了物件里邊存放的值,即物件里面的value陣列參考了"hello world"這個字串,又因為這個字串要存放到字串常量池中去,而在此之前池中已經有了"hello world"這個字串并且在前文中說道字串常量池是由哈希表構成的,所以這個物件就指向字串常量池中已有的字串常量,

案例二:
請看下一段代碼:
public static void main(String[] args) {
String str1 = "hello world";
String str2 = "hello " + "world";
System.out.println(str1 == str2);
}
答案是 true
為什么呢?
因為字串常量在編譯階段就已經實作了連接,即"hello " + “world” 變成了"hello world",又因為在執行str2這段代碼時,在堆中的字符常量池中已經有了"hello world"這個字串,當str2指向的這個已經連接好的字串將要存放到池中的時候,先要檢查池中是否已經有了將要存放的字串,如果有的話,str2就指向這個已有的字串,
不信的話,那么我們就反編譯一下,看是否在編譯的時候已經實作了拼接,

看圖說話:
常量在編譯的程序中,就已經被運算了,

案例三:
請閱讀一下代碼:
public static void main(String[] args) {
String str1 = "hello world";
String str2 = "hello " + new String("world");
System.out.println(str1 == str2);
}
答案是false
為什么呢?
因為當str2這段代碼執行的程序中參考了一個String物件,這個物件指向存放在字串常量池中放入字串"world",而這個被String物件所參考的字串"world"要和在池中的"hello"實作拼接,形成了一個新的物件,
至于拼接的程序這就牽連到了StringBuffer/StringBuilder知識點,我們在文章后半段介紹,而這個str3指向了這個新的物件,自然和str1所指向的"hello world"所在的地址不相同,
看圖說話:

案例四:
請閱讀以下代碼:
public static void main(String[] args) {
String str1 = "hello world";
String str2 = "hello ";
String str3 = str2 + "world";
System.out.println(str1 == str3);
}
答案是:false
為什么呢?
因為當我們執行到了str3這句代碼時,str2變成了變數,那么讓我們回憶一下,變數的概念是什么,所謂變數就是在編譯的時候不知道變數里邊是什么值,只有在運行的時候才知道里面是什么值,在字串拼接的程序中,會形成一個新的物件,str3就指向這個物件,str1指向的是一個字串,顯然他們指向的不是同一塊地址,所以是false.
看圖說話:

案例五:
請閱讀一下代碼:
public static void main(String[] args) {
String str1 = "hello world";
String str2 = new String("hello ") + new String("world");
System.out.println(str1 == str2);
}
當大家做到這個題的時候,就很容以判斷出,答案肯定是false,因為str1和str2所指向的不是同一塊地址,
看圖說話:

案例六:
請閱讀一下代碼:
public static void main(String[] args) {
String str1 = "hello world";
String str2 = new String("hello world");
str2.intern();
System.out.println(str1 == str2);
}
答案是:false
在這里就不得不介紹
intern()方法了,這個方法的作用是把一個字串手動填入字串常量池,
我們已知str2作為一個參考,它參考了一個String物件,在這個物件里面有一個value陣列,這個value陣列又參考了一個字串,這個字串"hello world"已經在字串常量池中已經有了,即使手動的填入這個已有的字串也是不行的,就是也為字串常量池是由哈希表實作的,在哈希表中,不允許有重復的值出現,所以str2.intern(),這段代碼,存不存在代碼的結果都是一樣的,等于它啥也沒干,
看圖說話:
它的記憶體圖和案例一一樣,在這里就不過多贅述,
案例七:
請閱讀一下代碼:
public static void main(String[] args) {
String str1 = new String("1") + new String("1");
str1.intern();
String str2 = "11";
System.out.println(str1 == str2);
}
答案是:true
為什么呢?
分析:
兩個物件所指向的字串都是"1",當兩個字串拼接之后形成了新的字串"11",然后手動的添加到字串常量池中,當代碼執行到str2這句代碼時,它所指向的字串"11",在池中已經有了,所以無需添加,這時候str2指向被拼接好的字串"11",假設這時候字串"11"的地址為0x123,那么指向它的物件這時候的地址也是0x123,所以str1和 str2的的地址相同
看圖說話:

案例八:
請閱讀一下代碼:
public static void main(String[] args) {
String str1 = new String("1") + new String("1");
String str2 = "11";
str1.intern();
System.out.println(str1 == str2);
}
答案是:false
為什么呢?
因為兩個物件拼接好的字串"11",然后str2指向字串"11",
而現在的字串常量池中里的字串"11"是str2這個參考指向的,現在手動的把拼接好的字串手動添加到字串常量池中,但是已經有了這個字符,所以手動添加不了
str1指向的是一個字串,str2指向了一個被參考的物件,他們的參考不相同,所以是false.

案例九:
請看一下代碼:
public static void func(String str) {
str = "bit";
}
public static void main(String[] args) {
String str = "gaobo";
func(str);
System.out.println(str);
}
答案是: gaobo
為什么呢?
因為我們這里的main方法中的str指向的是一個被存放到堆區中的字串常量池中的字串,而我們向func方法中傳去的是一個str參考,起初func方法中的str參考和main方法中的str參考同時指向同一塊地址,
但是在func方法中str這個參考指向了一個新的地址(即在堆區的字串常量池中的"bit"的地址),所以兩個不是指向的是一塊地址,所以他們不相同,依據上述的代碼我們可以知道最后列印的結果為"gaobo".
看圖說話:

案例十:
請看一下代碼:
public static void main(String[] args) {
String str1 = "Hello";
String str2 = str1;
str1 = "world";
System.out.println(str1 == str2);
}
答案是:false
為什么呢?
這個思路和上一個題的思路基本一致,str1是一個參考,這個參考指向堆區中的字串常量池中的"Hello",str2參考了str1所參考的物件(即字串"Hello")所以起初兩個參考指向同一塊地址,但是str1這個參考又指向了一塊新的地址(即"world"所在的地址空間),所以在比較兩個參考的時候肯定不相等,
看圖說話:

正確理解==和equal的區別
- 雙等于比較的是兩個字串參考是否相等,即他們所指向的地址是否相等
- equal是比較兩個字串中的內容是否相同,
equal方法的使用:
public static void main(String[] args) {
String str1 = "hello world";
String str2 = "hello world";
System.out.println(str1.equals(str2));
}
兩個字串內容相同,回傳true,否則回傳false.但是如果有一個字串為空,那也只能這樣寫了,
public static void main(String[] args) {
String str1 = null;
String str2 = "hello world";
System.out.println(str2.equals(str1));
//System.out.println("hello world".equal(str1));最好寫成這樣
}
因為如果把str1放到了equal()方法之前比較,就會發生空指標例外

理解字串不可變:
我們在文章前面也看到可String.java的原始碼,知道了String這個類(被final修飾)是一個密封類,不能被繼承,同時String所參考的物件也不能被修改,
字串是一種不可變的物件,它的內容不可修改
讓我們看一下下面的代碼:
String str = "hello" ;
str = str + " world" ;
str += "!!!" ;
System.out.println(str);
// 執行結果
hello world!!!
表面上看去,就直接在把要加的字串添加到原來的字串后面就好了,但是不是那樣的,
請詳細看他的記憶體圖:

在字串常量池中的字串,在拼接時,每拼接一次就會產生一個新的物件,當代碼執行到str = str + "world"時,str相當于一個變數,變數和字串拼接需要在堆中重新開辟一塊新的記憶體空間,而這個記憶體空間中的value陣列就會指向拼接好的字串"hello world",在拼接"!!!“的時候也是一樣的,還需要開辟一個新的記憶體空間,在新的記憶體空間中的value陣列就會指向拼接好的"hello world!!!”,在這是str這個參考指向拼接好的字串,
大家可以想一想,如果不斷地這樣兩個是不是就會造成空間浪費,只為得到最后一個拼接好的字串,那么為了拼接好的這個字串,那么在這個拼接好的字串后面是不是有著許許多多的字串為了拼接所開辟的內空間,使堆區的閑余空間減少,當我們介紹到了StringBuffer/StringBuilder的時候就不用這樣拼接一次開辟一次空間了,就直接在原字串的末尾拼接,
這樣也體現出了字串的不可變,不能在同一個字串上修改,
那么如果我們實在要對字串進行修改呢?
詳細請看一下代碼:
String str = "Hello";
str = "h" + str.substring(1);
System.out.println(str);
// 執行結果
hello
在這里就先介紹一下substring()方法,它的作用是對字串進行截取,根據下標,截取這個下標,這個下標之后所對應字串中的所用元素,
在上述代碼中就是截取了"hello",1下標之后的所有字串(“ello”),然后與字串"h"進行拼接,但是這樣也不是在字串"h"之后直接拼接的,還是需要在堆中重新開辟一塊空間,這個空間中的value陣列又指向了拼接好的"hello"字串,
那么我們真的想在原字串中,做出修改呢,那就必須簡單的介紹一下反射了(這里是簡單介紹)
反射是面向物件編程的一種重要特性,在有些語言中被稱為"自省"
反射的特性:反射是在程式運行的程序中,獲取修改某個物件的具體資訊(成員屬性,成員方法),相當于讓物件更好的認識自己,
那我們現在就利用反射的特性對字串做出修改,

我們可以從String類的原始碼中看到String所參考的欄位,是不可被修改的,并且這個欄位在類中是私有屬性,
String str = "hello world";
// 獲取位元組碼物件
Class c = String.class;
//獲取String類中的value欄位
Field field = c.getDeclaredField("value");
//修改字串的權限,使其權限變為true
field.setAccessible(true);
//獲取str中的val
char[] vals = (char[]) field.get(str);
//對字串進行修改
vals[0] = 'H';
System.out.println(vals);
運行結果:Hello world
并且是在原有的字串上進行修改,沒有重新開辟新的空間,
2.字符,位元組,字串
字符和字串
一.字符與字串
字串內部包含一個和字串一樣的char[]陣列,字串可以和字符進行相互轉化,
| No | 方法名稱 | 型別 | 描述 |
|---|---|---|---|
| 1 | public String(char[]chars) | 構造方法 | 將字符轉換成為字串 |
| 2 | public String(char[] chars,int offest,int count) | 構造方法 | offest表示偏移位置,count表示偏移量,把字符陣列中從offest之后偏移count個字符轉變為字串 |
| 3 | public charAt(int index) | 普通方法 | index表示下標,在字串中找到對應下標的字符 |
| 4 | public toCharArray(String str) | 普通方法 | 將字串轉變為字符陣列 |
方法一:
將字符轉換成為字串
public static void main(String[] args) {
char[] array = {'a', 'b', 'c', 'd'};
String str = new String(array);
System.out.println(str);
}
//運行結果:abcd
方法二:
從偏移點出發,把偏移量個字符轉變成為字串
public static void main(String[] args) {
char[] chars = {'a','b','c','d','e','f'};
String str = new String(chars,2,3);
System.out.println(str);
}
//運行結果:cd
方法三:
在字串中找到下標為i的字符
public static void main(String[] args) {
String str = "abcdefg";
char ch = str.charAt(5);
System.out.println(ch);
}
//找到下標為5的字符
//運行結果為:f
方法四:
將字串轉換成為字符陣列
public static void main(String[] args) {
String str = "abcdef";
char []chars = str.toCharArray();
System.out.println(Arrays.toString(chars));
}
//運行結果:[a,b,c,d,e,f]
方法小練:
判斷一個字串中是不是全是數字?
已知字串為"158946a9";
public static boolean isNUmber(String str){
if(str.length() == 0){ //判斷傳來的字串是不是長度為0
return false;
}
if(str == null){ //判斷傳來的字串是不是為null
return false;
}
for(int i = 0;i<str.length();i++){
if(str.charAt(i)>'9' || str.charAt(i) < '0'){
return false;
}
}
return true;
}
public static void main(String[] args) {
String str = "158946a9";
System.out.println(isNUmber(str));
}
//運行結果false
位元組與字串:
位元組常用于資料傳輸和編碼轉化,String也可以和位元組之間進行相互轉化
| NO | 方法名稱 | 型別 | 描述 |
|---|---|---|---|
| 1 | public String(Byte[] byte) | 構造方法 | 將位元組陣列轉換成為字串 |
| 2 | pubic String(Byte[] byte,int offest,int count) | 構造方法 | 從位元組陣列的offest的偏移點到count個位元組元素轉換成為字串 |
| 3 | public byte[] getBytes(String str) | 普通方法 | 將字串中的所有字符轉換成為位元組陣列 |
| 4 | public byte[] getBytes(String CharsetName)trows unspportedEncodingException | 普通方法 | 編碼處理 |
方法一:
將位元組陣列轉換成為字串
public static void main(String[] args) {
byte []bytes = {97,98,99,100};
String str = new String(bytes);
System.out.println(str);
}
//運行結果:abcd
方法二:
從位元組陣列的offest的偏移點到count個位元組元素轉換成為字串
public static void main(String[] args) {
byte[]bytes = {97,98,99,100,101,102};
String str = new String(bytes,1,3);
System.out.println(str);
}
//運行結果:bcd
方法三:
將字串中的所有字符轉換成為位元組陣列
public static void main(String[] args) {
String str = "abcdef";
byte[]bytes = str.getBytes();
System.out.println(Arrays.toString(bytes));
}
//運行結果:[977,98,99,100,101,102]
方法四:
編碼處理
1.UTF-8編碼:
public static void main(String[] args) java.io.UnsupportedEncodingException{
String str = "abcde高";
byte[]bytes = str.getBytes("UTF-8");
System.out.println(Arrays.toString(bytes));
}
//運行結果:[97, 98, 99, 100, 101, -23, -85, -104]
//在這里是UTF-8編碼,我們不知道他把漢字編譯成了什么
2.GBK編碼:
public static void main(String[] args)throws java.io.UnsupportedEncodingException {
String str = "abcde高";
byte[]bytes = str.getBytes("GBK");
System.out.println(Arrays.toString(bytes));
}
//運行結果:[97, 98, 99, 100, 101, -72, -33]
所以在UTF-8編碼和GBK編碼之后字符陣列的結果不同,
分析在什么的情況下使用字符陣列,在那種場合使用位元組陣列
- byte[] 是把 String 按照一個位元組一個位元組的方式處理, 這種適合在網路輸, 資料存盤這樣的場景下使用. 更適合針對二進制資料來操作.
- char[] 是吧 String 按照一個字符一個字符的方式處理, 更適合針對文本資料來操作, 尤其是包含中文的時候
3.字串常見操作:
字串比較:
我們在之前介紹的是字串比較函式equal(),它區分字符的大小寫,在這里我們將具體的介紹有關字串比較的方法
| NO | 方法介紹 | 型別 | 作用 |
|---|---|---|---|
| 1 | public boolean equal(String str) | 普通方法 | 比較字串(區分大小寫) |
| 2 | public boolean equalIgnoreCase(String str) | 普通方法 | 比較字串是否相等(不區分大小寫) |
| 3 | public int compareTo(String str) | 普通方法 | 比較字串大小如果一個字串比另一個字串大就回傳正數,否則回傳負數,相等的時候回傳0 |
方法一:
比較字串(區分大小寫)
public static void main(String[] args) {
String str = "hello";
String str1 = "Hello";
System.out.println(str.equals(str1));
}
//運行結果:false
方法二:
比較字串是否相等(不區分大小寫)
public static void main(String[] args) {
String str = "hello";
String str1 = "Hello";
System.out.println(str.equalsIgnoreCase(str1));
}
//回傳true
方法三:
比較字串大小如果一個字串比另一個字串大就回傳正數,否則回傳負數,相等的時候回傳0
public static void main12(String[] args) {
String str = "hello";
String str1 = "Hello";
System.out.println(str.compareTo(str1));
}
//運行結果 32
public static void main(String[] args) {
String str = "abcdef";
String str1 = "abc";
System.out.println(str.compareTo(str1));
}
//運行結果 3
不知道大家發現了沒有,如果字串的長度相等,compareTo()方法回傳的是字符的ASII碼的差值,當字串長度不相等時它回傳的是他們的長度之差,
那么讓我們看一下compareTo()方法的原始碼

字串拆分:
可以根據一個字串,以特定的字符進行分割,成為若干份子字串,
| No | 方法名稱 | 型別 | 作用 |
|---|---|---|---|
| 1 | public String split(String regex) | 普通 | 分割字串 |
| 2 | public String split(String regex,int limit) | 普通 | 分割字串,limit表示分割組數的極限值 |
方法一:
分割字串
public static void main(String[] args) {
String str = "a b c d e f";
String str1 = " ";
String[]strings = str.split(str1);
for (String s:strings) {
System.out.println(s);
}
}
//運行結果
//a
//b
//c
//d
//e
//f
方法二:
分割字串,limit表示分割組數的極限值
public static void main(String[] args) {
String str = "a b c d e f";
String str1 = " ";
String[]strings = str.split(2);
for (String s:strings) {
System.out.println(s);
}
}
//運行結果:
//a
//b c d e f
那當我們要對一個IP地址進行分割,還能和上邊的一樣嗎?
都這樣問了肯定是不會的啦!!!
public static void main(String[] args) {
String str = "19.15.5.4";
String []strings = str.split("\\.");//如果不添加轉義字符,列印的結果有誤
for (String s:strings) {
System.out.println(s);
}
}
//運行結果為:
//19
//15
//5
//4
注意事項:
當分割符為**"+","*","|“都要在前面加上轉移字符”/";**
如果是"",就在前面加上"//";
如果有多個分割符就要用"|"分開,
字串中有多個分割符
public static void main(String[] args) {
String str = "19&18%14#13";
String []strings = str.split("&|%|#");
for (String s:strings) {
System.out.println(s);
}
}
實作多組分割:
public static void main(String[] args) {
String str = "18#15#16&11&100";
String []strings = str.split("#");
for(String s1 : strings){
String []s = s1.split("&");
for(String ss: s){
System.out.println(ss);
}
}
}
//運行結果:
//18
//15
//16
//11
//100
字串替換:
使用一個特定新的字串來替換里一個字串,
| No | 方法名稱 | 型別 | 作用 |
|---|---|---|---|
| 1 | public String replace(String regex,String replacement) | 普通 | 替換字串 |
| 2 | public String replaceAll( String regex,String replacement) | 普通 | 替換字串中的所用特定的字串 |
| 3 | public String replaceFirst(String regex,String replacement) | 普通 | 替換首個內容 |
方法一:
替換字串
public static void main(String[] args) {
String str = "a b c d ";
String str1 = str.replace(" ","1");
System.out.println(str1);
}
//運行結果a1b1c1d1
方法二:
public static void main(String[] args) {
String str = "a b c d ";
String str1 = str.replaceAll(" ","1");
System.out.println(str1);
}
//方法一和方法二執行的結果相同
方法三:
替換首個內容
public static void main(String[] args) {
String str = "a b c d e";
String str1 = str.replaceFirst(" ","1");
System.out.println(str1);
}
//運行結果:a1b c d e
字串查找:
從一個完整的字串當中,判斷指定內容是否存在,
| No | 方法名稱 | 型別 | 作用 |
|---|---|---|---|
| 1 | public boolean contains( CharSequence str) | 普通方法 | 在一個字串中查找另一個字串 |
| 2 | public int indexOf(String str) | 普通方法 | 在一個字串中查找一個字串,找到了就回傳它的下標,如果沒有找到就回傳-1 |
| 3 | public int indexOf(String str,int fromIndex) | 普通方法 | 從指定位置開始查找字串,找到回傳索引,找不到回傳-1 |
| 4 | public int lastIndexOf(String str) | 普通方法 | 從后向前查找指定字串 |
| 5 | public int lastIndexOf(String str,int formIndex) | 普通方法 | 從指定位置從后向前查找字串 |
| 6 | public boolean startsWith(String prefix) | 普通方法 | 看是否已指定字串開頭 |
| 7 | public boolean startWith(String prefix,int formIndex) | 普通方法 | 從指定的位置開始判斷是否已特定的字串開頭 |
| 8 | public boolean endsWith(String suffix) | 普通方法 | 看是否以特定的字串結尾 |
方法一:
在一個字串中查找另一個字串
public static void main(String[] args) {
String str = "abcdef";
String str1 = "ab";
System.out.println(str.contains(str1));
}
//運行結果:true
方法二:
在一個字串中查找一個字串,找到了就回傳它的下標,如果沒有找到就回傳-1
public static void main(String[] args) {
String str = "ancdef";
String srr1 = "cd";
System.out.println(str.indexOf(srr1));
}
//運行結果:2
方法三:
從指定位置開始查找字串,找到回傳索引,找不到回傳-1
public static void main(String[] args) {
String str = "decfgh";
String str1 = "de";
System.out.println(str.indexOf(str1,2));
}
//運行結果:-1
方法四:
從后向前查找指定字串
public static void main(String[] args) {
String str = "abndefg";
String str1 = "nd";
System.out.println(str.lastIndexOf(str1));
}
//運行結果:2
方法五:
從指定位置從后向前查找字串
public static void main(String[] args) {
String str = "ababrag";
String str1 = "ab";
System.out.println(str.lastIndexOf(str1,2));
}
//運行結果:2
方法六:
看是否已指定字串開頭
public static void main(String[] args) {
String str = "abcdefgt";
String str1 = "abc";
System.out.println(str.startsWith(str1));
}
//運行結果:true
方法七:
從指定的位置開始判斷是否已特定的字串開頭
public static void main(String[] args) {
String str = "abcdef";
String str1 = "a";
System.out.println(str.startsWith(str1,1));
}
//運行結果:false
方法八:
看是否以特定的字串結尾
public static void main(String[] args) {
String str = "abcde";
String str1 = "de";
System.out.println(str.endsWith(str1));
}
//運行結果:true
字串截取:
截取字串中的一部分,
| No | 方法名稱 | 型別 | 作用 |
|---|---|---|---|
| 1 | public String subString(int index) | 普通 | 從index處截取字串 |
| 2 | public String subString(int index,int count) | 普通 | 截取index和count之間的字串,注意前閉后開區間 |
方法一:
從index處截取字串
public static void main(String[] args) {
String str = "abcdef";
String str1 = str.substring(2);
System.out.println(str1);
}
//運行結果cdef
方法二:
截取index和count之間的字串
public static void main(String[] args) {
String str = "abcdef";
String str1 = str.substring(2,5);
System.out.println(str1);
}
//運行結果:cde注意是[2,5)
其他操作方法:
| No | 方法名稱 | 型別 | 作用 |
|---|---|---|---|
| 1 | public String trim() | 普通 | 去除字串的左右空格,中間空格不變 |
| 2 | public String toUpperCase() | 普通 | 把字串中的字符全部變為大寫 |
| 3 | public String toLowerCase() | 普通 | 把字串中的字符全部變為小寫 |
| 4 | public native iterm() | 普通 | 把字串入池 |
| 5 | public String concat(String str) | 普通 | 字串連接,類似于"+" |
| 6 | public int length() | 普通 | 計算字串的長度 |
| 7 | public boolean IsEmpty() | 普通 | 判斷當前字串是否為空 |
**這下方法博主就不一一介紹了,大家自己在idea上獨自完成,每個方法的具體功能已經說得很清楚,**在我們之后寫代碼的程序中也會時常用到,
4.String,StringBuilder和StringBuffer之間的區別:
我們知道了String 是用來描述字串的,并且String 型別的變數指向的內容是不能修改的,雖然這樣會使程式變得安全,但是為我們在平時利用字串解題時帶來了諸多不便,在這是就衍生出了兩個StringBuilder和StringBuffer關鍵字,這兩個關鍵字所修飾的是可以字串改變的.并且StringBuilder和StringBuffer的方法基本相同,
我們知道String型別定義變數的方法有兩種,一種是直接賦值即(String str = “abc”),另一種是new一個物件,這個物件中的value陣列指向要添加進來的字串即(String str = new String(“abc”));但是在StringBuffer和StringBuilder中就只有一種賦值方法,就是進行構造,
StringBuffer sb = new StringBuffer("abc");
我們知道在String中連接字串的時候會在堆上產生一個新的物件,這個物件中的value陣列指向拼接好的字串,然而在被StringBuffer和StringBuilder所修飾的時候,如果要拼接字串,那么直接就在一個字串的末尾直接添加,不會產生新的物件,這樣就不會利用更多的空間去申請物件,耗費空間,
在StringBuffer和StringBuider中用來拼接字串的方法時append()方法
這里就利用StringBuffer介紹一下,為什么被他修飾字串就可以改變呢?
我們一起康康它的原始碼:
我們可以看到在append()方法所對應的原始碼中,最后回傳的是this.即當前的物件,所以我們就不用在對上重復的申請空間了,
請看一下代碼
public static void main(String[] args) {
String str = new String("abc");
for(int i = 0;i<10;i++){
str += i;
}
System.out.println(str);
}
請看原始碼:

雖然StringBuilder在這里實作了優化,但是我們每次回圈,都要new一個物件,所以我們以后千萬不要這樣寫代碼,
String和StringBuilder,StringBuffer之間的相互轉化
Stringhe StringBuilder,Stringbuffer之間不能進行直接轉化,
String變為StringBuffer:利用StringBuffer的構造方法或append()方法
StringBuffer變為String:呼叫toString()方法,
public static void main(String[] args) {
//String變成StringBuffer
String str = "hello";
StringBuffer stringBuffer = new StringBuffer(str);
//StringBuffer stringBuffer = new StringBuffer();
//String str1 = stringBuffer.append(str);
System.out.println(stringBuffer);
System.out.println(str1);
//運行結果為hello
}
public static void main(String[] args) {
//StringBuffer變為String
StringBuffer stringBuffer = new StringBuffer("abc");
String str = stringBuffer.toString();
System.out.println(str);
}
除了append()方法外,StringBuffer也有一些String沒有的方法,比如字串翻轉`,
public static void main(String[] args) {
StringBuffer stringBuffer = new StringBuffer();
StringBuffer str = stringBuffer.append("abcdef");
StringBuffer str1 = str.reverse();
System.out.println(str1);
//運行結果為fedcba
}
規定字串洗掉
public static void main(String[] args) {
StringBuffer stringBuffer = new StringBuffer();
StringBuffer str = stringBuffer.append("abcdef");
//洗掉規定字串
StringBuffer str1 = str.delete(2,4);
System.out.println(str1);
}
//運行結果aef
向字串中插入字符
public static void main(String[] args) {
StringBuffer stringBuffer = new StringBuffer();
StringBuffer str = stringBuffer.append("abcd");
StringBuffer str1 = str.insert(2,"dd");
System.out.println(str1);
}
//運行結果為abddcd
快接近尾聲了,那今天就利用一道經典的面試題來結束今天的博客吧!!
面試題:String,StringBuffer,StringBuilder之間的差別
String修飾的字串,不能被修改,而被StringBuffer和StringBuilder修飾的符串是可以進行修改的,
StringBuffer和StringBuilder之間的比較:StringBuffer采用同步處理,屬于執行緒安全操作;而StringBuilder未采用同步處理,屬于執行緒不安全操作
StringBuffer和StringBuilder所利用的大部分方法基本相同,
讓我們分別看一下StringBuffer和StringBuilder的原始碼!!
StringBuffer的原始碼:
StringBuilder的原始碼:
我們可以從StringBuffer和StringBuilder的原始碼比較可以看出,關于StringBuffer的方法,都被synchronized所修飾
那么我們就知道了synchronized的作用是什么了,他就是保證執行緒的安全,
StringBuffer一般用于多執行緒,StringBuilder一般用于單執行緒,
那有些童鞋就會問什么是執行緒安全,什么又是執行緒不安全呢?
博主在這里舉一個尷尬但是有助于理解的例子,
如果你在上廁所,廁所門外面有著一群人都等著你,大家都要上廁所,如果你沒有一把鎖把門鎖住,大家蜂擁的擠向廁所,正在如廁的你,不是就尷尬了嗎,而這個鎖就是
synchronized

轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/319703.html
標籤:java




