主頁 > 後端開發 > [java篇]一次性幫你搞懂String,StringBuffer,StringBuilder類

[java篇]一次性幫你搞懂String,StringBuffer,StringBuilder類

2021-10-17 08:21:51 後端開發

前言:在我們學習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型別的物件,而這個參考在堆疊上開辟空間,物件被存放在堆上,

看圖說話:
分析:

  1. str1直接賦值,把"hello world"這個字串傳給了str1,在堆中,凡是被雙引號引起來的常量,都會存放在字串常量池中,
  2. 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的區別

  1. 雙等于比較的是兩個字串參考是否相等,即他們所指向的地址是否相等
  2. 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方法名稱型別描述
1public String(char[]chars)構造方法將字符轉換成為字串
2public String(char[] chars,int offest,int count)構造方法offest表示偏移位置,count表示偏移量,把字符陣列中從offest之后偏移count個字符轉變為字串
3public charAt(int index)普通方法index表示下標,在字串中找到對應下標的字符
4public 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方法名稱型別描述
1public String(Byte[] byte)構造方法將位元組陣列轉換成為字串
2pubic String(Byte[] byte,int offest,int count)構造方法從位元組陣列的offest的偏移點到count個位元組元素轉換成為字串
3public byte[] getBytes(String str)普通方法將字串中的所有字符轉換成為位元組陣列
4public 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編碼之后字符陣列的結果不同,

分析在什么的情況下使用字符陣列,在那種場合使用位元組陣列

  1. byte[] 是把 String 按照一個位元組一個位元組的方式處理, 這種適合在網路輸, 資料存盤這樣的場景下使用. 更適合針對二進制資料來操作.
  2. char[] 是吧 String 按照一個字符一個字符的方式處理, 更適合針對文本資料來操作, 尤其是包含中文的時候

3.字串常見操作:

字串比較:

我們在之前介紹的是字串比較函式equal(),它區分字符的大小寫,在這里我們將具體的介紹有關字串比較的方法

NO方法介紹型別作用
1public boolean equal(String str)普通方法比較字串(區分大小寫)
2public boolean equalIgnoreCase(String str)普通方法比較字串是否相等(不區分大小寫)
3public 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方法名稱型別作用
1public String split(String regex)普通分割字串
2public 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方法名稱型別作用
1public String replace(String regex,String replacement)普通替換字串
2public String replaceAll( String regex,String replacement)普通替換字串中的所用特定的字串
3public 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方法名稱型別作用
1public boolean contains( CharSequence str)普通方法在一個字串中查找另一個字串
2public int indexOf(String str)普通方法在一個字串中查找一個字串,找到了就回傳它的下標,如果沒有找到就回傳-1
3public int indexOf(String str,int fromIndex)普通方法從指定位置開始查找字串,找到回傳索引,找不到回傳-1
4public int lastIndexOf(String str)普通方法從后向前查找指定字串
5public int lastIndexOf(String str,int formIndex)普通方法從指定位置從后向前查找字串
6public boolean startsWith(String prefix)普通方法看是否已指定字串開頭
7public boolean startWith(String prefix,int formIndex)普通方法從指定的位置開始判斷是否已特定的字串開頭
8public 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方法名稱型別作用
1public String subString(int index)普通從index處截取字串
2public 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方法名稱型別作用
1public String trim()普通去除字串的左右空格,中間空格不變
2public String toUpperCase()普通把字串中的字符全部變為大寫
3public String toLowerCase()普通把字串中的字符全部變為小寫
4public native iterm()普通把字串入池
5public String concat(String str)普通字串連接,類似于"+"
6public int length()普通計算字串的長度
7public 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

上一篇:半萬字長文學習 MySQL 主從復制原理,面試必問,建議收藏!

下一篇:JavaWeb 基礎知識(二)——執行緒01

標籤雲
其他(157675) Python(38076) JavaScript(25376) Java(17977) C(15215) 區塊鏈(8255) C#(7972) AI(7469) 爪哇(7425) MySQL(7132) html(6777) 基礎類(6313) sql(6102) 熊猫(6058) PHP(5869) 数组(5741) R(5409) Linux(5327) 反应(5209) 腳本語言(PerlPython)(5129) 非技術區(4971) Android(4554) 数据框(4311) css(4259) 节点.js(4032) C語言(3288) json(3245) 列表(3129) 扑(3119) C++語言(3117) 安卓(2998) 打字稿(2995) VBA(2789) Java相關(2746) 疑難問題(2699) 细绳(2522) 單片機工控(2479) iOS(2429) ASP.NET(2402) MongoDB(2323) 麻木的(2285) 正则表达式(2254) 字典(2211) 循环(2198) 迅速(2185) 擅长(2169) 镖(2155) 功能(1967) .NET技术(1958) Web開發(1951) python-3.x(1918) HtmlCss(1915) 弹簧靴(1913) C++(1909) xml(1889) PostgreSQL(1872) .NETCore(1853) 谷歌表格(1846) Unity3D(1843) for循环(1842)

熱門瀏覽
  • 【C++】Microsoft C++、C 和匯編程式檔案

    ......

    uj5u.com 2020-09-10 00:57:23 more
  • 例外宣告

    相比于斷言適用于排除邏輯上不可能存在的狀態,例外通常是用于邏輯上可能發生的錯誤。 例外宣告 Item 1:當函式不可能拋出例外或不能接受拋出例外時,使用noexcept 理由 如果不打算拋出例外的話,程式就會認為無法處理這種錯誤,并且應當盡早終止,如此可以有效地阻止例外的傳播與擴散。 示例 //不可 ......

    uj5u.com 2020-09-10 00:57:27 more
  • Codeforces 1400E Clear the Multiset(貪心 + 分治)

    鏈接:https://codeforces.com/problemset/problem/1400/E 來源:Codeforces 思路:給你一個陣列,現在你可以進行兩種操作,操作1:將一段沒有 0 的區間進行減一的操作,操作2:將 i 位置上的元素歸零。最終問:將這個陣列的全部元素歸零后操作的最少 ......

    uj5u.com 2020-09-10 00:57:30 more
  • UVA11610 【Reverse Prime】

    本人看到此題沒有翻譯,就附帶了一個自己的翻譯版本 思考 這一題,它的第一個要求是找出所有 $7$ 位反向質數及其質因數的個數。 我們應該需要質數篩篩選1~$10^{7}$的所有數,這里就不慢慢介紹了。但是,重讀題,我們突然發現反向質數都是 $7$ 位,而將它反過來后的數字卻是 $6$ 位數,這就說明 ......

    uj5u.com 2020-09-10 00:57:36 more
  • 統計區間素數數量

    1 #pragma GCC optimize(2) 2 #include <bits/stdc++.h> 3 using namespace std; 4 bool isprime[1000000010]; 5 vector<int> prime; 6 inline int getlist(int ......

    uj5u.com 2020-09-10 00:57:47 more
  • C/C++編程筆記:C++中的 const 變數詳解,教你正確認識const用法

    1、C中的const 1、區域const變數存放在堆疊區中,會分配記憶體(也就是說可以通過地址間接修改變數的值)。測驗代碼如下: 運行結果: 2、全域const變數存放在只讀資料段(不能通過地址修改,會發生寫入錯誤), 默認為外部聯編,可以給其他源檔案使用(需要用extern關鍵字修飾) 運行結果: ......

    uj5u.com 2020-09-10 00:58:04 more
  • 【C++犯錯記錄】VS2019 MFC添加資源不懂如何修改資源宏ID

    1. 首先在資源視圖中,添加資源 2. 點擊新添加的資源,復制自動生成的ID 3. 在解決方案資源管理器中找到Resource.h檔案,編輯,使用整個專案搜索和替換的方式快速替換 宏宣告 4. Ctrl+Shift+F 全域搜索,點擊查找全部,然后逐個替換 5. 為什么使用搜索替換而不使用屬性視窗直 ......

    uj5u.com 2020-09-10 00:59:11 more
  • 【C++犯錯記錄】VS2019 MFC不懂的批量添加資源

    1. 打開資源頭檔案Resource.h,在其中預先定義好宏 ID(不清楚其實ID值應該設定多少,可以先新建一個相同的資源項,再在這個資源的ID值的基礎上遞增即可) 2. 在資源視圖中選中專案資源,按F7編輯資源檔案,按 ID 型別 相對路徑的形式添加 資源。(別忘了先把檔案拷貝到專案中的res檔案 ......

    uj5u.com 2020-09-10 01:00:19 more
  • C/C++編程筆記:關于C++的參考型別,專供新手入門使用

    今天要講的是C++中我最喜歡的一個用法——參考,也叫別名。 參考就是給一個變數名取一個變數名,方便我們間接地使用這個變數。我們可以給一個變數創建N個參考,這N + 1個變數共享了同一塊記憶體區域。(參考型別的變數會占用記憶體空間,占用的記憶體空間的大小和指標型別的大小是相同的。雖然參考是一個物件的別名,但 ......

    uj5u.com 2020-09-10 01:00:22 more
  • 【C/C++編程筆記】從頭開始學習C ++:初學者完整指南

    眾所周知,C ++的學習曲線陡峭,但是花時間學習這種語言將為您的職業帶來奇跡,并使您與其他開發人員區分開。您會更輕松地學習新語言,形成真正的解決問題的技能,并在編程的基礎上打下堅實的基礎。 C ++將幫助您養成良好的編程習慣(即清晰一致的編碼風格,在撰寫代碼時注釋代碼,并限制類內部的可見性),并且由 ......

    uj5u.com 2020-09-10 01:00:41 more
最新发布
  • Rust中的智能指標:Box<T> Rc<T> Arc<T> Cell<T> RefCell<T> Weak

    Rust中的智能指標是什么 智能指標(smart pointers)是一類資料結構,是擁有資料所有權和額外功能的指標。是指標的進一步發展 指標(pointer)是一個包含記憶體地址的變數的通用概念。這個地址參考,或 ” 指向”(points at)一些其 他資料 。參考以 & 符號為標志并借用了他們所 ......

    uj5u.com 2023-04-20 07:24:10 more
  • Java的值傳遞和參考傳遞

    值傳遞不會改變本身,參考傳遞(如果傳遞的值需要實體化到堆里)如果發生修改了會改變本身。 1.基本資料型別都是值傳遞 package com.example.basic; public class Test { public static void main(String[] args) { int ......

    uj5u.com 2023-04-20 07:24:04 more
  • [2]SpinalHDL教程——Scala簡單入門

    第一個 Scala 程式 shell里面輸入 $ scala scala> 1 + 1 res0: Int = 2 scala> println("Hello World!") Hello World! 檔案形式 object HelloWorld { /* 這是我的第一個 Scala 程式 * 以 ......

    uj5u.com 2023-04-20 07:23:58 more
  • 理解函式指標和回呼函式

    理解 函式指標 指向函式的指標。比如: 理解函式指標的偽代碼 void (*p)(int type, char *data); // 定義一個函式指標p void func(int type, char *data); // 宣告一個函式func p = func; // 將指標p指向函式func ......

    uj5u.com 2023-04-20 07:23:52 more
  • Django筆記二十五之資料庫函式之日期函式

    本文首發于公眾號:Hunter后端 原文鏈接:Django筆記二十五之資料庫函式之日期函式 日期函式主要介紹兩個大類,Extract() 和 Trunc() Extract() 函式作用是提取日期,比如我們可以提取一個日期欄位的年份,月份,日等資料 Trunc() 的作用則是截取,比如 2022-0 ......

    uj5u.com 2023-04-20 07:23:45 more
  • 一天吃透JVM面試八股文

    什么是JVM? JVM,全稱Java Virtual Machine(Java虛擬機),是通過在實際的計算機上仿真模擬各種計算機功能來實作的。由一套位元組碼指令集、一組暫存器、一個堆疊、一個垃圾回收堆和一個存盤方法域等組成。JVM屏蔽了與作業系統平臺相關的資訊,使得Java程式只需要生成在Java虛擬機 ......

    uj5u.com 2023-04-20 07:23:31 more
  • 使用Java接入小程式訂閱訊息!

    更新完微信服務號的模板訊息之后,我又趕緊把微信小程式的訂閱訊息給實作了!之前我一直以為微信小程式也是要企業才能申請,沒想到小程式個人就能申請。 訊息推送平臺🔥推送下發【郵件】【短信】【微信服務號】【微信小程式】【企業微信】【釘釘】等訊息型別。 https://gitee.com/zhongfuch ......

    uj5u.com 2023-04-20 07:22:59 more
  • java -- 緩沖流、轉換流、序列化流

    緩沖流 緩沖流, 也叫高效流, 按照資料型別分類: 位元組緩沖流:BufferedInputStream,BufferedOutputStream 字符緩沖流:BufferedReader,BufferedWriter 緩沖流的基本原理,是在創建流物件時,會創建一個內置的默認大小的緩沖區陣列,通過緩沖 ......

    uj5u.com 2023-04-20 07:22:49 more
  • Java-SpringBoot-Range請求頭設定實作視頻分段傳輸

    老實說,人太懶了,現在基本都不喜歡寫筆記了,但是網上有關Range請求頭的文章都太水了 下面是抄的一段StackOverflow的代碼...自己大修改過的,寫的注釋挺全的,應該直接看得懂,就不解釋了 寫的不好...只是希望能給視頻網站開發的新手一點點幫助吧. 業務場景:視頻分段傳輸、視頻多段傳輸(理 ......

    uj5u.com 2023-04-20 07:22:42 more
  • Windows 10開發教程_編程入門自學教程_菜鳥教程-免費教程分享

    教程簡介 Windows 10開發入門教程 - 從簡單的步驟了解Windows 10開發,從基本到高級概念,包括簡介,UWP,第一個應用程式,商店,XAML控制元件,資料系結,XAML性能,自適應設計,自適應UI,自適應代碼,檔案管理,SQLite資料庫,應用程式到應用程式通信,應用程式本地化,應用程式 ......

    uj5u.com 2023-04-20 07:22:35 more