J2SE入門(三) String深度解析

  String能夠說是Java中使用最多最頻繁、最特殊的類,由於同時也是字面常量,而字面常量包括基本類型、String類型、空類型。
  
  一. String的使用
  
  1. String的不可變性
  
  /**
  
  * The {@code String} class represents character strings. All
  
  * string literals in Java programs, such as {@code "abc"}, are
  
  * implemented as instances of this class.
  
  * <p>
  
  * Strings are constant; their values cannot be changed after they
  
  * are created. String buffers support mutable strings.
  
  * Because String objects are immutable they can be shared. For example:
  
  * ...
  
  */
  
  public final class String {
  
  private final char value[];
  
  }
  
  String對象一旦在堆中建立出來,就沒法再修改。由於String對象放在char數組中,該數組由final關鍵字修飾,不可變。
  
  2. 定義一個字符串
  
  /**
  
  * 定義一個字符串
  
  */
  
  String str1 = "helloworld";
  
  String str2 = "helloworld";
  
  //也能夠,但基本不這樣寫
  
  String str3 = new String("helloworld");
  
  System.out.println(str1 == str2);
  
  System.out.println(str1 == str3);
  
  //運行結果 true, false
  
  上面三句代碼怎麼理解呢?這裏須要先引入一個概念,字符串常量池。
  
  字符串常量池是一塊特殊的獨立內存空間,放在Java Heap中 { 在Jdk7.0以前字符串常量池存放在PermGen中,Jdk7.0的時候移動到了Java Heap(在堆中仍然是獨立的),Jdk8.0的時候去掉了PermGen,用Metaspace進行取代 } ,Java內存模型不是本章討論的重點。
  
  str1和str2引用的字符串字面量就在字符串常量池中,而str3引用的對象在Java Heap中。
  
  怎麼,還不太好理解?舉個例子
  
  工做一天,到下班時間了,準備看會兒金瓶.,算了,《三國演義》,打開小說網站,在線閱讀;過了半個小時,女票回家了,看《三國演義》也是她想作的事兒,我看網址發給她,好,她也開始看了,再過半個小時,我爸回來了,他也是三國迷,可是他不喜歡在線看,所以在書店買了一本看。
  
  上面提到的小說網站就是一個字符串常量池,包含了不少字符串字面量,如《三國演義》、《西遊記》、《紅樓夢》等,每一個字符串字面量在常量池中保持獨一份,不管誰進網站看《三國演義》都是一樣的網址和一樣的內容。
  
  我和女票就是str1和str2,咱們看的都是同一個網站的《三國演義》,不只內容同樣,引用的地址也同樣(字符串常量池中保留了惟一的「helloworld」),所以str1 == str2 運行結果爲true
  
  而我爸就是str3,與我和女票都不同,雖然看的內容也是《三國演義》,可是經過實體書籍來看,引用地址不同,同時一本書籍不支持多我的同時看(字符串對象在java heap中,且每次new都會新建一個對象),所以str1 == str3 運行結果爲false。
  
  一個字符串字面量老是引用String類的同一個實例,由於被String.intern()方法限定了,一樣咱們能夠調用該方法將堆中的String對象放到字符串常量池中,這樣作能夠提高內存使用效率,同時可讓所用使用者共享惟一的實例。
  
  System.out.println(str1 == str3.intern());
  
  //運行結果爲true
  
  那麼該方法的實現邏輯是怎麼樣的呢,咱們看一下源碼
  
  /**
  
  * Returns a canonical representation for the string object.
  
  * <p>
  
  * A pool of strings, initially empty, is maintained privately by the
  
  * class {@code String}.
  
  * <p>
  
  * When the intern method is invoked, if the pool already contains a
  
  * string equal to this {@code String} object as determined by
  
  * the {@link #equals(Object)} method, then the string from the pool is
  
  * returned. Otherwise, this {@code String} object is added to the
  
  * pool and a reference to this {@code String} object is returned.
  
  * <p>
  
  * It follows that for any two strings {@code s} and {@code t},
  
  * {@code s.intern(www.yuchengyuLe.com) == t.intern()} is {@code true}
  
  * if and only if {@code s.equals(t)} is {@code true}.
  
  * <p>
  
  * All literal strings and string-valued constant expressions are
  
  * interned. String literals are defined in section 3.10.5 of the
  
  * <cite>The Java&trade; Language Specification<www.chaoyuL.com  /cite>.
  
  *
  
  * @return  a string that has the same contents as this string, but is
  
  *          guaranteed to be from a pool of unique strings.
  
  */
  
  public native String intern();
  
  咱們發現這是一個native方法,看一下注釋,發現str3.intern()方法大體流程是:
  
  當執行intern()時,會先判斷字符串常量池中是否含有相同(經過equals方法)的字符串字面量,若是有直接返回字符串字面量;若是不含,則將該字符串對象添加到字符串常量池中,同時返回該對象在字符串常量池的引用。
  
  返回的引用須要賦值纔可,不然仍是會指向堆中的地址,即:
  
  String str4 = new String("helloChina");
  
  System.out.println(str4.intern(www.chenghyLpt.com) == str4);//false
  
  str4 = str4.intern(www.xingtuyLgw.com);
  
  String str5 = "helloChina";
  
  String str6 = "helloZhonghua"
  
  System.out.println(str4 == str5);//true
  
  下面咱們看一下內存結構
  
  3. 再次賦值給已定義的字符串
  
  str6 = "helloHuaxia";
  
  咱們開始已經說了String是由final關鍵字修飾,不可變,那麼此時在內存中如何體現呢?
  
  4. String 對 「+」 的處理
  
  String str7 = "good good" + " study";
  
  String str8 = "good good study";
  
  system.out.println(str7 == str8);
  
  經過編譯工具後獲得
  
  String str7 = "good good study";
  
  String str8 = "good good study";
  
  所以咱們能夠發現編譯器在編譯期間就是進行變量合併,而不會在常量池中建立三個對象 「good good」,「 study」,"good good study"。str7 == str8 運行結果 true。
  
  但若是這樣
  
  String str9 = "good good ";
  
  String str10 = str9 + "study";
  
  system.out.println(str8 == str10);//false
  
  這時運行結果爲false,經過String變量 + 字符常量方式獲得的結果會在堆中,不在常量池中,固然能夠經過intern()方法放進常量池中,同時不只「+」如此,調用substring(),toUpperCase(),trim()等返回的都是String在堆中的地址。
  
  5. String經常使用的方法
  
  //str1 == "hello,world ";
  
  //獲取長度
  
  str1.length()//12;
  
  //截取位置2到5之間的字符串(包括位置2,不包括位置5,從0開始)
  
  str1.substring(2,5);//"llo"
  
  //判斷是否含有字符串「ello」
  
  str1.contains("ello");//true,經過indexOf實現
  
  //獲取ello在str1中的開始位置
  
  str1.indexOf("ello");//1
  
  //將字符串轉化爲字符串數據
  
  str1.split(",");//["hello","world"]
  
  //去掉字符串兩側空格
  
  str1.trim();//"hello,world"
  
  二. 總結
  
  本文從String的不可變性,String建立時字面量和String對象的不一樣,字符串字面量常量池,字符串的內存結構,經常使用的String相關方法的描述,如有不對之處,請批評指正,望共同進步,謝謝!java

相關文章
相關標籤/搜索