夯實Java基礎(十三)——字符串

字符串應該是咱們在Java中用的最頻繁、最多的,可見字符串對於咱們來講是多麼的重要,因此咱們很是有必要去深刻的瞭解一下。正則表達式

一、String

String就表明字符串,在Java中字符串屬於對象。咱們剛剛接觸Java時,在學習數據類型的時候應該提到過String。Java有基本數據類型和引用數據類型,而String就是一個引用數據類型,它是一個類,既然它是一個類,那咱們就來看看它的源碼結構。數組

從上面的圖能夠看出,String類是用final修飾的,代表它不能再被繼承了,由於String這個類中對字符串的操做的方法已經很是豐富,不須要咱們再去擴展功能了。同時還實現了序列化、比較排序、字符序列這三個接口,代表字符串能夠被序列化、能夠用於比較排序。關於實現了CharSequence接口下面有講到。咱們還能夠看到String類中定義了一個char型數組value[ ],這個是用於存儲字符串的內容,而且使用final修飾的,代表這個是常量。因此字符串一旦被初始化,就不能夠被改變,表示String是不可變性,這就致使每次對String的操做都會生成新的String對象。安全


關於String實現CharSequence接口這裏,我去百度一下:多線程

CharSequence是一個接口,表示char值的一個可讀序列。此接口對許多不一樣種類的char序列提供統一的自讀訪問。此接口不修改該equals和hashCode方法的常規協定,所以,一般未定義比較實現 CharSequence 的兩個對象的結果。他有幾個實現類:CharBuffer、String、StringBuffer、StringBuilder。app

CharSequence與String都能用於定義字符串,但CharSequence的值是可讀可寫序列,而String的值是隻讀序列。性能

對於一個抽象類或者是接口類,不能使用new來進行賦值,可是能夠經過如下的方式來進行實例的建立:學習

CharSequence cs="hello";測試

實例:ui

 1 public class StringTest {
 2     public static void main(String[] args) {
 3         String str="String";
 4         StringBuffer sBuffer=new StringBuffer("StringBuffer");
 5         StringBuilder sBuilder=new StringBuilder("StringBuilder");
 6         show(str);
 7         show(sBuffer);
 8         show(sBuilder);
 9     }
10     //若是參數類型爲String則不能接收StringBuffer和StringBuilder
11     public static void show(CharSequence cs){
12         System.out.println(cs);
13     }
14 }

運行結果:spa

多是這樣理解的吧,CharSequence是一個接口,自己是沒有什麼讀寫意義的。String只是它的一個實現類,雖然String是隻讀,可是CharSequence的實現類還有StringBuffer,StringBuilder這些可寫的,因此用CharSequence做爲參數能夠接收String,StringBuffer,StringBuilder這些類型。

參考:https://blog.csdn.net/a78270528/article/details/46785949

二、String的實例和拼接

String的實例:

建立String的實例有兩種方式,一種是直接給String的變量賦值,另外一種是使用String的構造器建立實例。那麼這兩種方式建立的實例有什麼區別呢?區別就是前者會建立一個對象,然後者會建立兩個對象。

舉例:

 1 public class StringTest {
 2     public static void main(String[] args) {
 3         //方式一:直接賦值
 4         String s1="abc";
 5         String s2="abc";
 6         //方式二:new+構造器
 7         String s3=new String("abc");
 8         String s4=new String("abc");
 9         System.out.println(s1 == s2);//true
10         System.out.println(s1 == s3);//false
11         System.out.println(s1 == s4);//false
12         System.out.println(s3 == s4);//false
13         System.out.println(s1.equals(s3));//true
14     }
15 }

運行結果一目瞭然,String的值是常量,它的值是放在方法區的處理池中,而常量池中相同的值在只會存在一份。咱們知道==比較的地址,equal()比較的是內容,s1和s2指向同一個引用,因此地址相同,而s3和s4它們分別建立了兩個對象,地址值顯然不一樣。

經過這個圖咱們也容易分析出建立實例時建立了幾個對象。

String s1 = "abc"建立對象的過程:

首先檢查常量池中是否存在內容爲"abc"的字符串,若是有,則再也不建立對象,直接讓s1變量指向該字符串的引用,若是沒有則在常量池中建立"abc"對象而後讓s1引用該對象。

String s3 = new String("abc")建立實例的過程:

首先在堆建立一個String的對象,並讓s3引用指向該對象,而後再到常量池中查看是否存在內容爲"abc"字符串對象,若是存在,則將String對象中的value引用指向常量對象,將new出來的字符串對象與字符串常量池中的對象聯繫起來,若是不存在,則在字符串常量池中建立一個內容爲"abc"的字符串對象,並將堆中的String對象與之聯繫起來。


 String的拼接:

字符串的拼接有三種方式:直接使用"+"、使用concat()方法、使用append()方法。這裏我主要來討論一下"+"問題,舉例:

 1 public class StringTest {
 2     public static void main(String[] args) {
 3         String s1="Hello";
 4         String s2="World";
 5 
 6         String s3="HelloWorld";
 7         String s4="Hello"+"World";
 8         String s5=s1+"World";
 9         String s6="Hello"+s2;
10         String s7=s1+s2;
11 
12         System.out.println(s3==s4);//true
13         System.out.println(s3==s5);//false
14         System.out.println(s3==s6);//false
15         System.out.println(s3==s7);//false
16         System.out.println(s5==s6);//false
17         System.out.println(s5==s7);//false
18         System.out.println(s6==s7);//false
19 
20         String s8=s7.intern();
21         System.out.println(s3==s8);//true
22 
23     }
24 }

從運行結果咱們能夠得出結論:①、常量與常量的拼接結果是常量,它們在在常量池中完成。②、只要涉及到有變量的(很是量),結果都是在堆內存中完成的。③、若是拼接後的結果調用了intern()方法,則返回值就是在常量池中。

三、String中經常使用的方法

String中的經常使用方法咱們須要熟練的掌握, 這樣平時作一些字符串的常規操做就能夠快速知道,而不用去查找API了。

①、常規:

  • int length():返回字符串的長度: return value.length
  • boolean isEmpty():判斷是不是空字符串:return value.length == 0
  • String trim():返回字符串的副本,忽略前導空白和尾部空白
  • String concat(String str):將指定字符串鏈接到此字符串的結尾。 等價於用「+」
  • String toLowerCase():使用默認語言環境,將 String 中的全部字符轉換爲小寫
  • String toUpperCase():使用默認語言環境,將 String 中的全部字符轉換爲大寫
  • char[] toCharArray():將字符串轉爲char型數組。
②、比較:
  • boolean equals(Object obj):比較字符串的內容是否相同
  • boolean equalsIgnoreCase(String anotherString):與equals方法相似,忽略大小寫
  • int compareTo(String anotherString):比較兩個字符串的大小
  • boolean matches(String regex):判斷此字符串是否匹配給定的正則表達式。
③、查找:
  • char charAt(int index): 返回某索引處的字符return value[index]
  • int indexOf(String str):返回指定子字符串在此字符串中第一次出現處的索引。注:indexOf和lastIndexOf方法若是未找到都是返回-1
  • int indexOf(String str, int fromIndex):返回指定子字符串在此字符串中第一次出現處的索引,從指定的索引開始
  • int lastIndexOf(String str):返回指定子字符串在此字符串中最右邊出現處的索引
  • int lastIndexOf(String str, int fromIndex):返回指定子字符串在此字符串中最後一次出現處的索引,從指定的索引開始反向搜索
  • boolean endsWith(String suffix):測試此字符串是否以指定的後綴結束
  • boolean startsWith(String prefix):測試此字符串是否以指定的前綴開始
  • boolean startsWith(String prefix, int toffset):測試此字符串從指定索引開始的子字符串是否以指定前綴開始
  • boolean contains(CharSequence s):當且僅當此字符串包含指定的 char 值序列時,返回 true
④、替換:
  • String replace(char oldChar, char newChar):返回一個新的字符串,它是經過用 newChar 替換此字符串中出現的全部 oldChar 獲得的。
  • String replace(CharSequence target, CharSequence replacement):使用指定的字面值替換序列替換此字符串全部匹配字面值目標序列的子字符串。
  • String replaceAll(String regex, String replacement):使用給定的 replacement 替換此字符串全部匹配給定的正則表達式的子字符串。
  • String replaceFirst(String regex, String replacement):使用給定的 replacement 替換此字符串匹配給定的正則表達式的第一個子字符串。
⑤、截取:
  • String substring(int beginIndex):返回一個新的字符串,它是此字符串的從beginIndex開始截取到最後的一個子字符串。
  • String substring(int beginIndex, int endIndex) :返回一個新字符串,它是此字符串從beginIndex開始截取到endIndex(不包含)的一個子字符串。
⑥、切片:
  • String[] split(String regex):根據給定正則表達式的匹配拆分此字符串。
  • String[] split(String regex, int limit):根據匹配給定的正則表達式來拆分此字符串,最多不超過limit個,若是超過了,剩下的所有都放到最後一個元素中。

若是還想學習更多的方法,能夠自行去看String的API。

四、StringBuffer和StringBuilder

StringBuffer和StringBuilder十分類似,它們都表明可變的字符序列,能夠對字符序列進行增刪改查操做,此時不會產生新的對象,並且它兩內部的方法也是同樣的。那麼String、StringBuffer和StringBuilder的區別是什麼。

String(JDK1.0):字符串常量,不可變字符序列,線程安全,效率低。

StringBuffer(JDK1.0):字符串變量,可變字符序列,線程安全,效率低。

StringBuilder(JDK5.0):字符串變量,可變字符序列,線程不安全,效率高。

爲何說String和StringBuffer是線程安全,StringBuilder是線程不安全呢?

由於String是final修飾的常量,它是不可變的字符串,全部的操做都是不可能改變它的值,因此線程是安全的。再經過看StringBuffer和StringBuilder的源碼,能夠很明顯發現,StringBuffer是線程安全的,由於其下的全部方法都加上了synchronized。而StringBuilder則沒有加這個關鍵字。

它們之間經常使用的方法:

  • StringBuffer append(xxx):用於進行字符串的拼接
  • cahr charAt(int index):返回char在指定索引在這個序列值。 
  • StringBuffer delete(int start,int end):刪除指定位置的內容
  • StringBuffer deleteCharAt(int index):刪除char在這個序列中的指定位置。 
  • StringBuffer replace(int start,int end,String str):把[start,end)位置上的元素替換爲str
  • void sedtCharAt(int n,cahr ch):將指定索引位置的字符改爲ch
  • StringBuffer insert(int offset,xxx):在指定位置插入xxx
  • StringBuffer reverse():將字符序列反轉
  • int indexOf(String str):返回指定子字符串第一次出現的字符串內的索引。
  • int lastIndexOf(String str):返回指定子字符串最右邊出現的字符串內的索引。 
  • String subString(int start,int end):返回一個新的 String,其中包含此序列中當前包含的字符的子序列。 

五、String、StringBuffer和StringBuilder三者效率對比

String、StringBuffer和StringBuilder涉及可變序列與不可變序列、線程是否安全狀況,這些因素必然影響到它們之間的運行效率,因此咱們來比較一下他們之間的運行效率。

簡單示例:

 1 public class StringTest {
 2     public static void main(String[] args) {
 3         long startTime=0L;
 4         long endTime=0L;
 5         String text=" ";
 6         StringBuffer buffer=new StringBuffer("");
 7         StringBuilder builder = new StringBuilder("");
 8 
 9         //String
10         startTime=System.currentTimeMillis();
11         for (int i = 0; i < 50000; i++) {
12             text=text+i;
13         }
14         endTime=System.currentTimeMillis();
15         System.out.println("String執行時間:"+(endTime-startTime));
16 
17         //StringBuffer
18         startTime=System.currentTimeMillis();
19         for (int i = 0; i < 50000; i++) {
20             buffer.append(String.valueOf(i));
21         }
22         endTime=System.currentTimeMillis();
23         System.out.println("StringBuffer執行時間:"+(endTime-startTime));
24 
25         //StringBuilder
26         startTime=System.currentTimeMillis();
27         for (int i = 0; i < 50000; i++) {
28             builder.append(String.valueOf(i));
29         }
30         endTime=System.currentTimeMillis();
31         System.out.println("StringBuilder執行時間:"+(endTime-startTime));
32     }
33 }

運行結果可能須要等個5-8秒,運行結果以下:

咱們屢次運行的結果也大體相同,因此運行效率爲:StringBuilder > StringBuffer > String。String如此的慢是由於它是字符串常量,在建立對象後是不可改變的,然而每次改變String類型的值都會在常量池中新建一個常量對象,因此很是耗時間。而StringBuffer和StringBuilder的可變的字符序列,它們只是在原有內容發生了改變,並無新建立對象。因此常常改變內容的字符串最好不要用 String類型,推薦使用StringBuffer,由於每次生成對象都會對系統性能產生影響,特別當內存中無引用對象多了之後, JVM 的 GC 就會開始工做,性能就會下降。

六、小結

經過上面的學習,咱們簡單小結一下:

String:適用於少許字符串操做的狀況。

StringBuffer:適用多線程下在字符緩衝區進行大量操做的狀況。

StringBuilder:適用於單線程下在字符緩衝區進行大量操做的狀況。

相關文章
相關標籤/搜索