Java String之你不知道的事

0.String 類

字符串普遍應用 在Java 編程中,在 Java 中字符串屬於對象,Java 提供了 String 類來建立和操做字符串。編程

本文講述的String類基於jdk1.8
複製代碼

1.直接賦值與new String()區別

在平時代碼中,經常看到String的兩種聲明賦值方式,可是其中又包含着怎樣的貓膩呢?兩種方式又有着怎樣的區別呢?請看下面代碼示例:bash

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);
    }
}
複製代碼

輸出結果以下:dom

false
false
true
複製代碼

咱們先來看一下代碼涉及的內存模型圖:測試

咱們來逐行分析下:

1.String s1 = new String("hh");在編譯時期,「hh」被編譯到.class文件中的Constant pool中;類加載時期,判斷字符串常量池是否存在「hh」,此時不存在,則從Constant pool取出「hh」存入字符串常量池。運行時期,在棧中建立s1引用,在堆中建立對象,對象內容爲「hh」,s1指向堆中的地址1。spa

2.String s2 = new String("hh");在編譯時期,「hh」被編譯到.class文件中的Constant pool中;類加載時期,判斷字符串常量池是否存在「hh」,此時存在,則不存入字符串常量池。運行時期,在棧中建立s2引用,在堆中建立對象,對象內容爲「hh」,s2指向堆中的地址2。3d

3.String s3 = "hh";在編譯時期,「hh」被編譯到.class文件中的Constant pool中;類加載時期,判斷字符串常量池是否存在「hh」,此時存在,則不存入字符串常量池。運行時期,在棧中建立s3引用,s3指向字符串常量池中的「hh」。code

4.String s4 = "hh";在編譯時期,「hh」被編譯到.class文件中的Constant pool中;類加載時期,判斷字符串常量池是否存在「hh」,此時存在,則不存入字符串常量池。運行時期,在棧中建立s4引用,s4指向字符串常量池中的「hh」。cdn

此時咱們再來分析下結果:對象

1.System.out.println(s1==s2);由於「==」判斷引用的地址是否相同,s1與s2在堆中的引用地址不相同,所以爲false。blog

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();
}
複製代碼

本文中的內容歡迎你們討論,若有偏頗歡迎指正,文中例子是爲了方面講解特地舉的,若有不當之處望諒解。

相關文章
相關標籤/搜索