淺析Java String

  • String 特性

    1.其定義的字符串序列不可變。java

    2.是一個final類,不可被繼承,且其內部一些重要方法被定義爲final類型,不可重寫。安全

    3.內部實現Serializable接口(支持字符串序列化)和Comparable接口(支持字符串比較大小)。性能優化

    4.內部定義了final char [ ] value 用於存儲字符串數據。jvm


  • String的實例化方式

1.字面量賦值的形式實例化:
    String str1 = "abc"
2.用 new  +  構造器形式實例化:
    String str2 = new String("abc")

下面來分析一下兩種不一樣實例化方式的區別:性能

​ 當咱們執行System.out.println(s1 == s2);的時候,輸出結果爲`false,優化

​ 而執行System.out.println(s1.equals(s2));的時候,輸出結果爲true,線程

​ 這和虛擬機的內存分配有關:
code

​ 對於str1字面量賦值的形式來講,字符串常量是存放在常量池中。而對於str2的構造器賦值形式,堆中的value存放的是new String("abc")對象自己,而str2是棧中開闢的一個內存塊,他裏面存放了指向對象自己的引用地址。有一點須要知道,在常量池中存放的東西都是惟一的,不會出現兩個相同的內容,這也是爲了減小內存開銷和提高jvm的性能優化,因此在使用str2 的時候,對象自己又會到常量池中找是否有abc,若是沒有則建立新的,若是有,則直接使用。對象

​ 在以前的文章中也探究過==equals的區別,當用==比較的時候,對於基本數據類型,比較的是內容,值是否相等。而對於剛剛的str1和str2,他們都是引用型數據類型,用==比較的時候,比較的是地址,很明顯,str1的地址直接指向常量池中的abc,而str2 的地址是指向堆內存中的實例對象,因此==比較確定是false,而用equals比較的時候,結果爲true,這是由於String類對object類的equals方法進行了重寫,object類中的equals方法底層用的仍是==來判斷地址值。blog

總結區別

​ 1.字面量賦值的形式實例化,字符常量內容存於常量池,變量存於棧中,直接指向常量池。

​ 2.new + 構造器形式實例化,會先在堆中建立實例對象,引用對象存於棧中,而後再去常量 區尋找須要的字符常量,若是找到了,直接使用,沒找到則開闢新的空間並存儲內容。


  • String的不可變性

    咱們都知道String是不可變的序列,那爲何不可變,又是怎麼實現的呢?先來看一段源碼:
public final class String
    implements java.io.Serializable, Comparable<String>, CharSequence {
    /** The value is used for character storage. */
    private final char value[];

    /** Cache the hash code for the string */
    private int hash; // Default to 0

    /** use serialVersionUID from JDK 1.0.2 for interoperability */
    private static final long serialVersionUID = -6849794470754667710L;

    private static final ObjectStreamField[] serialPersistentFields =
        new ObjectStreamField[0];

​ 能夠看出,String是一個final類,也就意味着他不能夠被繼承,並且其內部成員變量是private修飾,方法也是final修飾,一樣也就意味着他的成員變量不會直接暴露給用戶,方法不能夠被重寫。這樣實現了方法不可變(不能重寫),變量不能變,好比private final char value[];這裏就是string字符串不能變的實現。

字符串不可變底層實現分析:

​ 當運行以下代碼的時候:

String s1 = "Java";
String s2 = "Hello";
String s5 = s1;
s5 = "change";
String s3 = new String("Hello");
String s4 = new String("Java");

​ 字符串常量在虛擬機內存空間的狀況如圖所示:

​ 可見,對於String s1 = "Java"這種字面量賦值的形式,會直接在常量池中開闢一個空間用於存儲相應的字符串(前提是常量池中尚未該字符串),而String s3 = new String("Hello")這樣的,會先在堆中建立對象,而後再去常量池中找是否有須要的字符常量,若是有,則直接使用,若是沒有,也一樣須要開闢新的空間來存儲。

重點看 :

String s1 = "Java";
String s5 = s1;
s5 = "change";

​ 當執行String s5 = s1時,s5會直接去使用s1在常量池中的內容,然後面當執行s5 = "change"的時候,也就是說須要對Java這個字符串進行修改,但是這個字符串除了s5本身使用外,s1也在使用,因此就不能直接修改他,而是要在空間中從新開闢一個空間,用於存儲change。這就是字符串不能夠直接修改的底層實現!

字符串設置爲不可變的緣由:

​ ①出於安全考慮,程序在運行以前虛擬機會把字符常量,靜態變量等預加載到常量池(方法區) 中存儲起來,在程序運行的時候直接調用,可是常量池裏面的信息不會有重複的,每個都是 惟一的(這樣是爲了減小內存的開銷,提高性能),這些信息是線程共享的,同一個字符串可 能會被多個線程使用,若是字符串可變,當某個線程修對他作了修改,其餘正在使用該字符串 的線程可能就會出現嚴重的錯誤,從而變得不安全。

​ ②保證hash值不會常常變更,具備惟一性,使得相似HashMap的容器能實現key—value的功能


  • String 字符串的拼接

    static String s1 = "Hello";
      static String s2 = "Java";
      static String s3 = "Hello"+"Java";
      static String s4 = "HelloJava";
      static String s5 = s1 + "Java";
      static String s6 = "Hello" + s2;
      static String s7 = s1 + s2;
      static String s8 = (s1 + s2).intern();
    內存分配如圖:

字符串拼接總結:

​ 1.常量和常量的拼接,結果也在常量池中,且不存在兩個相同的常量。

​ 2.只要參與拼接的項裏面有變量,結果就在堆中。

​ 3.使用(String).inter()方法處理拼接後,被處理的字符串會進入常量池中。


  • 說在最後

    文章僅是筆者的我的理解,不免存在許多不完善和理解不恰當之處,歡迎批評指正。

    碼字不易,創做辛苦,歡迎轉載分享,請註明出處。

    交流歡迎Q我:321662487

相關文章
相關標籤/搜索