0.String 類
字串廣泛應用 在Java 程式設計中,在 Java 中字串屬於物件,Java 提供了 String 類來建立和操作字串。
本文講述的String類基於jdk1.8
複製程式碼
1.直接賦值與new String()區別
在平時程式碼中,常常看到String的兩種宣告賦值方式,但是其中又包含著怎樣的貓膩呢?兩種方式又有著怎樣的區別呢?請看下面程式碼示例:
public class TestString {
public static void main(String[] args) {
String s1 = new String("hh");
String s2 = new String("hh");
String s3 = "hh";
String s4 = "hh";
System.out.println(s1==s2);
System.out.println(s1==s3);
System.out.println(s3==s4);
}
}
複製程式碼
輸出結果如下:
false
false
true
複製程式碼
我們先來看一下程式碼涉及的記憶體模型圖:
我們來逐行分析下:1.String s1 = new String("hh");
在編譯時期,“hh”被編譯到.class檔案中的Constant pool
中;類載入時期,判斷字串常量池是否存在“hh”,此時不存在,則從Constant pool
取出“hh”存入字串常量池。執行時期,在棧中建立s1引用,在堆中建立物件,物件內容為“hh”,s1指向堆中的地址1。
2.String s2 = new String("hh");
在編譯時期,“hh”被編譯到.class檔案中的Constant pool
中;類載入時期,判斷字串常量池是否存在“hh”,此時存在,則不存入字串常量池。執行時期,在棧中建立s2引用,在堆中建立物件,物件內容為“hh”,s2指向堆中的地址2。
3.String s3 = "hh";
在編譯時期,“hh”被編譯到.class檔案中的Constant pool
中;類載入時期,判斷字串常量池是否存在“hh”,此時存在,則不存入字串常量池。執行時期,在棧中建立s3引用,s3指向字串常量池中的“hh”。
4.String s4 = "hh";
在編譯時期,“hh”被編譯到.class檔案中的Constant pool
中;類載入時期,判斷字串常量池是否存在“hh”,此時存在,則不存入字串常量池。執行時期,在棧中建立s4引用,s4指向字串常量池中的“hh”。
此時我們再來分析下結果:
1.System.out.println(s1==s2);
因為“==”判斷引用的地址是否相同,s1與s2在堆中的引用地址不相同,因此為false。
2.System.out.println(s1==s3);
s1的引用地址指向堆中地址1,而s3的引用地址指向字串常量池中的“hh”,因此為false。
3.System.out.println(s3==s4);
s3的引用地址指向字串常量池中的“hh”,s4的引用地址指向字串常量池中的“hh”,因此為true。
**總結:**new String()與直接賦值都會在字串放入常量池之前進行判斷,如果不存在則放入,如果存在則不放入。但是,new String()會在堆中多開闢一塊空間儲存字串的值。因此,我們在賦值過程中應多使用直接賦值。
2.String的intern()
intern() 方法返回字串物件的規範化表示形式。這個方法會首先檢查字串池中是否有這個字串,如果存在則返回這個字串的引用,否則就將這個字串新增到字串池中,然會返回這個字串的引用。即不會在堆中建立物件。
它遵循以下規則:對於任意兩個字串 s 和 t,當且僅當 s.equals(t) 為 true 時,s.intern() == t.intern() 才為 true。
測試程式碼如下:
String a = new String("ab");
String b = new String("ab");
String c = "ab";
String d = "a" + "b";
String e = "b";
String f = "a" + e;
System.out.println(b.intern() == a);
System.out.println(b.intern() == c);
System.out.println(b.intern() == d);
System.out.println(b.intern() == f);
System.out.println(b.intern() == a.intern());
複製程式碼
結果如下:
false
true
true
false
true
複製程式碼
我們來逐行分析下結果:
1.b.intern() == a;
b.intern()引用指向常量池中的引用,a引用指向堆中的引用,因此為false。
2.b.intern() == c;
b.intern()引用指向常量池中的引用,c引用指向常量池中的引用,因此為true。
3.b.intern() == d;
b.intern()引用指向常量池中的引用,d在編譯期間會將“a”+“b”編譯為“ab”,因此與d與c相同,因此為true。
4.b.intern() == f;
b.intern()引用指向常量池中的引用,f為帶有變數的操作,因此在編譯期間不能存入常量池中,f的引用不指向常量池,因此為false。
5.b.intern() == a.intern();
b.intern()引用指向常量池中的引用,a.intern()引用指向常量池中的引用,因此為true。
intern()使用場景
由於字串中如果含有變數的字串拼接,該字串不會進入常量池中,因此會存在大量相同的字串。使用intern()可以在執行時期將字串放入常量池,返回字串引用,避免重複建立字串。
例子參考:
static final int MAX = 1000 * 10000;
static final String[] arr = new String[MAX];
public static void main(String[] args) throws Exception {
Integer[] DB_DATA = new Integer[10];
Random random = new Random(10 * 10000);
for (int i = 0; i < DB_DATA.length; i++) {
DB_DATA[i] = random.nextInt();
}
long t = System.currentTimeMillis();
for (int i = 0; i < MAX; i++) {
//將字串放入常量池並返回引用
arr[i] = new String(String.valueOf(DB_DATA[i % DB_DATA.length])).intern();
}
System.out.println((System.currentTimeMillis() - t) + "ms");
System.gc();
}
複製程式碼
本文中的內容歡迎大家討論,如有偏頗歡迎指正,文中例子是為了方面講解特意舉的,如有不當之處望諒解。