Java String 探索

今天看到了一個Java string的相關問題,解決問題的過程當中就想把string 好好理順了,總結在這裏。數組

== 和 equals()

== 是判斷兩個變量是否指向同一個對象,equals()只判斷兩個字符串內容是否相同安全

public class Cons {
    public static void main(String[] args) throws InterruptedException {

        String s2 = new String("vv");
        String s3 = "vv";
        System.out.println(s2 == s3);//false
        System.out.println(s3.equals(s2));//true
    }
}

String、StringBuilder和StringBuffer

String和StringBuilder:StringBuilder是可變的,也就是說用StringBuilder建立的字符串你能夠隨時改變它。
StringBuilder和StringBuffer:StringBuffer是同步的,它是線程安全(thread-safe)的,但效率要比StringBuilder差得多。函數

查看String的構造函數jdk源碼:工具

public String(StringBuffer buffer) {
    synchronized(buffer) {
        this.value = Arrays.copyOf(buffer.getValue(), buffer.length());
    }
}

public String(StringBuilder builder) {
    this.value = Arrays.copyOf(builder.getValue(), builder.length());
}

可見 爲了使buffer的線程安全性在構造String時獲得延續,加了同步塊。ui

String str = new String("vv"); 建立了幾個對象?

答案:1或2
new String("vv")在堆中建立了1個實例對象,而另1個就是放在常量池中的 "vv" 對象,固然這裏的str自己 只是一個引用,放在棧裏,用來指向堆中建立出來的對象。因此若是常量池已經有"vv" 對象,就只在堆中建立一個對象;若是尚未,就會放入常量池,而後再在堆中建立一個對象,怎麼驗證呢?this

public class Cons {
        public static void main(String[] args) throws InterruptedException {
            String s1 = new String("vv");
            String s2 = "vv";
            System.out.println(s1 == s2);//false
        }
    }

而後用命令行工具( 深刻理解Java虛擬機 一書中看的工具)
clipboard.pngspa

可見常量池中有一個String 類型的 對象 vv,並且new出來的對象不是指向常量池的那個對象,亦即新建立了一個命令行

注:jdk1.7 之後,虛擬機把存儲Java對象的地方定義爲堆,其它地方是不會有Java對象的實體的。故常量池再也不存儲對象實例,而是存儲的引用,實際對象仍是在堆中,因此有所不一樣,下文再也不贅述。線程

String str = "vv"; 建立了幾個對象?

答案:0或1
若是常量池已經有"vv" 對象,就直接返回引用,若是尚未,就會放入常量池,而後返回引用。code

public class Cons {
        public static void main(String[] args) throws InterruptedException {
            String s1 = "vv";
            String s2 = "vv";
            System.out.println(s1 == s2);//true
        }
    }

可見s1,s2指向同一個對象。
clipboard.png

並且常量池也有 vv

String str = "v" + "v";建立了幾個對象?

答案:0或1
常量字符串是在編譯的時候就被肯定的,"v"是常量,因此編譯時肯定
這個代碼編譯後 與 String str = "vv"; 是同樣的

public class Cons {
        public static void main(String[] args) throws InterruptedException {
            String s1 = "vv";
            String s2 = "v"+"v";
            System.out.println(s1 == s2);//true
        }
    }

可見 s1,s2 指向同一個對象

String str = s1 + s2;建立了幾個對象?

答案 視狀況而定
「+」鏈接的兩個字符串自己就是字面常量字符串時,若是池中存在這樣鏈接後的字符串,則是不會從新建立對象,而是直接引用池中的字符串對象;若是「+」鏈接的兩字符串中只要有一個是變量,是會產生新的字符串對象。

public class Cons {
        public static void main(String[] args) throws InterruptedException {
            String s1 = "ww";
            String s2 = "vv";
            String s3 = "vvww";
            String s4 = "vv"+"ww";
            String s5 = "vv"+s1;
            System.out.println(s3 == s4);//true
            System.out.println(s3 == s5);//false
        }
    }

clipboard.png

可是若是變量是常量時,就不一樣了

public class Cons {
        public static void main(String[] args) throws InterruptedException {
            final String  s1 = "ww";
            String s2 = "vv";
            String s3 = "vvww";
            String s4 = "vv"+"ww";
            String s5 = "vv"+s1;
            String s6 = s2+s1;
            System.out.println(s3 == s4);//true
            System.out.println(s3 == s5);//true
        }
    }

但若是先定義final字符串,但未在定義處初始化,那麼又不一樣了,

public class Cons {
        public static void main(String[] args) throws InterruptedException {
            final String  s1 ;
            String s2 = "vv";
            String s3 = "vvww";
            String s4 = "vv"+"ww";
            s1 = "ww";
            String s5 = "vv"+s1;
            String s6 = s2+s1;
            System.out.println(s3 == s4);//true
            System.out.println(s3 == s5);//false
        }
    }

由於s1是在運行過程肯定的,因此s5也只能運行時肯定;
總結起來, String str=s1+s2 建立幾個變量,關鍵取決於 s1,s2 可否在編譯期肯定

String str = "v".concat("v");建立了幾個對象?

public class Cons {
    public static void main(String[] args) throws InterruptedException {
        String s1 = new String("vv");
        String s2 = "v".concat("v");
        String s4 = "v"+"v";
        String s3 = "vv";
        System.out.println(s2 == s3);//false
        System.out.println(s1 == s3);//false
        System.out.println(s4 == s3);//true
    }
}

可見concat 產生的變量沒有直接引用常量池的對象。

查看jdk8源碼

public String concat(String str) {
    int otherLen = str.length();
    if (otherLen == 0) {
        return this;
    }
    int len = value.length;
    char buf[] = Arrays.copyOf(value, len + otherLen);
    str.getChars(buf, len);
    return new String(buf, true);
}

果真是新建立了一個 String對象。

String 和 Char[]

查看jdk源碼,知道 String的內部實現就是一個 Char 數組, 說String 不可變,也是由於 這個數組就是一個final 類型的 變量。

clipboard.png

未完待續......

參考
http://jiangzhengjun.iteye.co...
《深刻理解Java虛擬機》

歡迎訪問個人我的主頁 mageek(mageek.cn)

相關文章
相關標籤/搜索