今天看到了一個Java string的相關問題,解決問題的過程當中就想把string 好好理順了,總結在這裏。數組
== 是判斷兩個變量是否指向同一個對象,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: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
答案: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虛擬機 一書中看的工具)
spa
可見常量池中有一個String 類型的 對象 vv,並且new出來的對象不是指向常量池的那個對象,亦即新建立了一個命令行
注:jdk1.7 之後,虛擬機把存儲Java對象的地方定義爲堆,其它地方是不會有Java對象的實體的。故常量池再也不存儲對象實例,而是存儲的引用,實際對象仍是在堆中,因此有所不一樣,下文再也不贅述。線程
答案: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指向同一個對象。
並且常量池也有 vv
答案: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 指向同一個對象
答案 視狀況而定
「+」鏈接的兩個字符串自己就是字面常量字符串時,若是池中存在這樣鏈接後的字符串,則是不會從新建立對象,而是直接引用池中的字符串對象;若是「+」鏈接的兩字符串中只要有一個是變量,是會產生新的字符串對象。
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 } }
可是若是變量是常量時,就不一樣了
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 可否在編譯期肯定
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對象。
查看jdk源碼,知道 String的內部實現就是一個 Char 數組, 說String 不可變,也是由於 這個數組就是一個final 類型的 變量。
參考
http://jiangzhengjun.iteye.co...
《深刻理解Java虛擬機》
歡迎訪問個人我的主頁 mageek(mageek.cn)