String底層解析


目錄

關於String

String類型的底層代碼是Java所有底層代碼中相對來說比較簡單、好理解。同時,String底層的源碼又是面試官經典的“面試第一題”。“良好的開端就是成功的一半”,因此這個問題回答的好壞會很大程度影響你接下來的面試。我會在這篇博客中梳理幾個面試中頻率較高的知識點。

String內部結構

String內部存儲結構為char數組,有兩個私有變量,一個是char[],哈希值。
具體結構:

 class String implements Serializable,Comparable<String>,CharSequence {
         //用於存儲字符串的值
       private char value[];
          //緩字符串的hash code
       private int hash;
}

String的構造方法

String的構造方法有四個,前兩個比較常見,參數是String字符串和char[]數組。
后兩個是容易被忽略的小透明,參數是StringBuffer和StringBuilder

1.String為參數的構造方法

//String為參數的構造方法
      public String(String original){
          this.value = original.value;
          this.hash = original.hash;
     }

2.char[] 為參數的構造方法

//char[]為參數的構造方法
      public String(char value[]){
         this.value = Arrays.copyOf(value,value.length);
      }

3.StringBuffer為參數的構造方法

//StringBuffer為參數的構造方法
      public String(StringBuffer buffer){
          synchronized(buffer){
              this.value=Array.copyOf(buffer.getValue(),buffer.length())
          }
      }

4.StringBuilder為參數的構造方法

//StirngBuilder為參數的構造方法
      public String(StringBuilder builder){
           this.value = Arrays.copyOf(builder.getValue(),builder.length());
      }

String中的對比——equals()和compareTo()的對比

equals()方法

String中的equals()方法是重寫了Object類中的equals()方法,其本質是利用了“==”。

equals()方法先檢驗對比雙方的引用地址是否相等(“==”),如果地址相等,對比雙方自然相等,返回true。然后檢驗需要對比的對象是否為String類型(“instanceof”),如果不是則返回false。之后對比兩個字符串的長度是否對等(“==”),最后將兩個字符串都轉化為char[]形式,循環對比每一個字符。

源碼:

public boolean equals(Object anObject){
           //對象引用相同直接返回true
           if(this==anObject){
               return true;
           }
          //判斷需要對比的值是否為String類型,如果不是則返回false
           if(anObject instanceof String){
               String anotherString = (String)anObject;
               int n = value.length;
               if(n==anotherString.value.length){
                   //把兩個字符串都轉化為char數組對比
                   char v1[]=value;
                   char v2[]=anotherString.value;
                   int i=0;
                   //循環比對兩個字符串的每一個字符
                   while(n--!=0){
                       //如果其中有一個字符不相等就true false,否則繼續對比
                       if(v1[i]!=v2[i])
                           return false;
                       i++;
                   }
                   return true;
               }
           }
          return false;
      }

compareTo()方法

compareTo()方法不涉及地址的對比,它只是循環比較所有的字符串。當兩個字符串中有任意一個字符不相同的時候,返回兩個字符的差值。如果長度不對等則返回兩個字符串長度的差值。

源碼:

public int compareTo(String anotherString){
           int len1 = value.length;
           int len2 = anotherString.value.length;
           //獲取到兩個字符串長度最短的那個int值
           int lim = Math.min(len1,len2);
           char v1[]=value;
           char v2[]=anotherString.value;
           int k=0;
           //對比每一個字符
           while(k<lim){
               char c1=v1[k];
               char c2=v2[k];
              if(c1!=c2){
                   //有字符不相等就返回差值
                   return c1-c2;
               }
               k++;
           }
           //檢驗長度
           return len1-len2;
      }

小結:String中compareTo()和equals()方法的異同點

不同點:

  • equals()可以接收一個Object類型的參數,而compareTo()只能接收String類型的參數
  • equals()返回值為Boolean,而compareTo()的返回值則為int

相同點:

  • 兩者都可以用於兩個字符串的比較。當equals()方法返回true時,或是compareTo()方法返回0時,則表示兩個字符串完全相同

String的常用方法清單

  • indexOf():查詢字符串首次出現的下標位置
  • lastIndexOf():查詢字符串最后出現的下標位置
  • contains(): 查詢字符串中是否包含另一個字符串
  • toLowerCase(): 把字符串全部轉換成小寫
  • toUpperCase(): 把字符串全部轉換成大寫
  • length(): 查詢字符串的長度
  • trim(): 去掉字符串首尾空格
  • replace():替換字符串中的某些字符
  • split(): 把字符串分割並返回字符串數組
  • join(): 把字符串數組轉為字符串

關於equals()方法:“==”和equals()的區別?

“==”:

  • 對於基本數據類型來說,是比較兩者的是否相等
  • 對於引用數據類型來說,是用於比較兩者引用地址是否相等

equals():

  • String類型的equals()是重寫了Object類中的equals()方法,他的基本實現是靠的“==”

Object類中的equal方法:

public boolean equals(Object obj){
               return (this==obj);
           }

為什么用final修飾String類?

對於這個問題,Java之父James Gosling在一次記者采訪中說明過,大體的原因如下:

1.安全
迫使String類被設計成不可變類的一個原因就是安全。(在進行系統的校驗工作中,如果設為可變類,就有可能會出現嚴重的系統崩潰問題。)

舉例:字符串常量池

2.高效
高司令是這樣回答的:他會更傾向於使用不可變類(final),因為它能夠緩存結果,當你在傳參時不需要考慮誰會修改它的值。如果是可變類的話,則有可能需要重新拷貝出來一個新值進行傳參,這樣性能上就會有一定的損失。

String和StringBuilder、StringBuffer的區別

首先,考慮到安全和性能問題,String類是不可變的,所以在字符串拼接的時候如果使用String的話性能會很低。因此就需要使用另一個數據類型StringBuffer。

StringBuffer:

  • 它提供了append和insert方法可用於字符串的拼接
  • 它使用了synchronized來保證線程安全

StringBuffer中append()方法:

			   public synchronized StringBuffer append(Object obj){
                   toStringCache = null;
                   super.append(String.valueOf(obj));
                   return this;
               }
               public synchronized StringBuffer append(String str){
                  toStringCache = null;
                   super.append(String.valueOf(str));
                   return this;
               }

但是由於StringBuffer保證了線程的安全,所以性能上來講 —— 很低。
於是在JDK1.5中推出了線程不安全,但是性能相對而言較高的StringBuilder。其功能和StringBuffer一樣。

String兩種創建方法的異同

我們先來看看創建String的兩種方式:

方式一:

String s1 = "java";  //直接賦值

方式二:

String s2 = new String("java");  //對象創建

這兩種方法都可以創建字符串,但是兩個方法在JVM的存儲區域截然不同

方法一:
jvm會先到字符串常量池中尋找是否有相同的字符串,如果有就返回常量句柄;如果沒有該字符串,就先在常量池中創建此字符串,然后再返回常量句柄。

方法二:
直接在堆中創建此字符串,只有調用intern()才會放入字符串常量池中。

舉例:

		   String s1 = new String("java");
           String s2 = s1.intern();
           String s3 = "java";
           System.out.println(s1==s2); //false
           System.out.println(s2==s3); //true

(s2,s3指向堆中常量池的“java”,s1指向堆中的“java”對象)

== 補充:jdk1.7之后把永生代換成了元空間,把字符串常量池從方法區移到了java堆上 ==

編譯器對String字符串的優化

我們經常會用“+”來拼接兩個字符串。即使是這樣拼接的字符串,在進行比較時,也不會出現和結果相同字符串不對等的情況。這是編譯器對於String的優化。

舉例:

			String s1 = "ja" + "va";
            String s2 = "java";
            System.out.println(s1==s2); //true


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM