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() == 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™ Language Specification</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() == str4);//false
str4 = str4.intern();
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相關方法的描述,若有不對之處,請批評指正,望共同進步,謝謝!