String底層解析

目錄

關於String

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

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[]形式,循環對比每個字符。安全

源碼:app

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()方法不涉及地址的對比,它只是循環比較全部的字符串。當兩個字符串中有任意一個字符不相同的時候,返回兩個字符的差值。若是長度不對等則返回兩個字符串長度的差值。jvm

源碼:性能

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

相同點:ui

  • 二者均可以用於兩個字符串的比較。當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
相關文章
相關標籤/搜索