相關題目(運行結果在代碼注釋后面)
1、
package StringTest; public class test1 { public static void main(String[] args){ String a = "a1";//“a1”在編譯的時候就能確定,所以編譯的時候,a1被放進了常量池中,同時a指向常量池中的a1對象 String b = "a"+ 1;//a和1這兩個常量都能在編譯時確定,所以他們相加的結果也能確定,因此編譯器檢查常量池中是否有值為a1的String對象,發現有了,因此b也指向常量池中的a1對象 System.out.println(a==b);//==判斷的是a和b是否指向同一個對象,也就是同一塊內存區域 }//true }
2、
package StringTest; public class test2 { public static void main(String[] args){ String a = "ab"; String bb = "b"; String b = "a"+ bb; //編譯器不能確定為常量 System.out.println(a==b); }//false }
3、
package StringTest; public class test3 { public static void main(String[] args){ String a = "ab"; final String bb = "b"; String b = "a"+ bb; //bb加final后是常量,可以在編譯器確定b System.out.println(a==b); }//true }
4、
package StringTest; public class test4 { public static void main(String[] args){ String a = "ab"; final String bb = getBB(); String b = "a"+ bb;//bb是通過函數返回的,雖然知道它是final的,但不知道具體是啥,要到運行期才知道bb的值 System.out.println(a==b); }//false private static String getBB(){ return "b"; } }
5、
package StringTest; public class test5 { private static String a = "ab"; public static void main(String[] args){ String s1 = "a"; String s2 = "b"; String s = s1 + s2;//+的用法 System.out.println(s == a); System.out.println(s.intern() == a);
//intern的含義(intern()用來返回常量池中的某字符串,如果常量池中已經存在該字符串,則直接返回常量池中該對象的引用,否則,在常量池中加入該對象,然后 返回引用) }//flase true }
6、
package StringTest; public class test6 { private static String a = new String("ab"); public static void main(String[] args){ String s1 = "a"; String s2 = "b"; String s = s1 + s2; System.out.println(s == a); System.out.println(s.intern() == a); System.out.println(s.intern() == a.intern()); }//flase false true }
詳解
1、String使用private final char value[]來實現字符串的存儲,也就是說String對象創建之后,就不能再修改此對象中存儲的字符串內容,所以說String類型是不可變的(immutable);
2、單獨使用""引號創建的字符串都是常量,編譯期就已經確定存儲到String Pool中;
3、使用new String("")創建的對象會存儲到heap中,是運行期新創建的;
new創建字符串時首先查看池中是否有相同值的字符串,如果有,則拷貝一份到堆中,然后返回堆中的地址;如果池中沒有,則在堆中創建一份,然后返回堆中的地址
(注意,此時不需要從堆中復制到池中,否則,將使得堆中的字符串永遠是池中的子集,導致浪費池的空間)
4、使用只包含常量的字符串連接符如"aa" + "aa"創建的也是常量,編譯期就能確定,已經確定存儲到String Pool中;
5、使用包含變量的字符串連接符如"aa" + s1創建的對象是運行期才創建的,存儲在heap中;
6、關於equals和==
(1)對於==,如果作用於基本數據類型的變量(byte,short,char,int,long,float,double,boolean ),則直接比較其存儲的"值"是否相等;如果作用於引用類型的變量(String),則比較的是所指向的對象的地址(即是否指向同一個對象)。
(2)equals方法是基類Object中的方法,因此對於所有的繼承於Object的類都會有該方法。在Object類中,equals方法是用來比較兩個對象的引用是否相等,即是否指向同一個對象。
(3)對於equals方法,注意:equals方法不能作用於基本數據類型的變量。如果沒有對equals方法進行重寫,則比較的是引用類型的變量所指向的對象的地址;而String類對equals方法進行了重寫,用來比較指向的字符串對象所存儲的字符串是否相等。其他的一些類諸如Double,Date,Integer等,都對equals方法進行了重寫用來比較指向的對象所存儲的內容是否相等。
7、String、StringBuffer、StringBuilder的區別
(1)可變與不可變:String是不可變字符串對象,StringBuilder和StringBuffer是可變字符串對象(其內部的字符數組長度可變)。
(2)是否多線程安全:String中的對象是不可變的,也就可以理解為常量,顯然線程安全。StringBuffer 與 StringBuilder 中的方法和功能完全是等價的,只是StringBuffer 中的方法大都采用了synchronized 關鍵字進行修飾,因此是線程安全的,而 StringBuilder 沒有這個修飾,可以被認為是非線程安全的。
(3)String、StringBuilder、StringBuffer三者的執行效率:
StringBuilder > StringBuffer > String 當然這個是相對的,不一定在所有情況下都是這樣。
比如String str = "hello"+ "world"的效率就比 StringBuilder st = new StringBuilder().append("hello").append("world")要高。因此,這三個類是各有利弊,應當根據不同的情況來進行選擇使用:
當字符串相加操作或者改動較少的情況下,建議使用 String str="hello"這種形式;
當字符串相加操作較多的情況下,建議使用StringBuilder,如果采用了多線程,則使用StringBuffer。