今天大致的閱讀了String類的源碼,並刷了常見的面試題,在此做個筆記。
面試題一:判斷下列程序運行結果
package String_test; public class test_1 { public static void main(String[] args) { String str1 = "HelloWorld"; String str2 = "HelloWorld"; String str3 = new String("HelloWorld"); String str4 = "Hello"; String str5 = "World"; String str6 = "Hello" + "World"; String str7 = str4 + str5; System.out.println("str1 == str2 result: " + (str1 == str2)); //1. true System.out.println("str1 == str3 result: " + (str1 == str3)); //2. false System.out.println("str1 == str6 result: " + (str1 == str6)); //3. true System.out.println("str1 == str7 result: " + (str1 == str7)); //4. false System.out.println("str1 == str7.intern() result: " + (str1 == str7.intern())); //5. true System.out.println("str3 == str3.intern() result: " + (str3 == str3.intern())); //6.false } }
畫內存圖逐個的分析每種情況:第一個:str1與str2指向同一個地址,故相等。
第二個:new代表創建了一個對象,str3指向堆內存中的引用,故str1與str3指向的地址不同。需要注意的是:字符串常量池中不可能存在兩個一樣的字符串值,向這里堆內存指向的實際還是運行時常量池中的HelloWorld值
第三個:由於“Hello”與“World”都是常量,用+號在編譯時會被自動編譯成String str6 = "HelloWorld",所以兩個引用都是指向常量池中的地址
第四個:關鍵點在於理解String str7 = str4+str5。在jdk文檔中有這么一段話
java語言提供了字符串串聯運算符特殊支持( + ),和其他對象轉換為字符串。字符串連接是通過
StringBuilder
實施(或StringBuffer
)類及其append
方法。字符串的轉換是通過方法toString
實施,由Object
和繼承的所有類的java。
可見jvm會在堆中創建一個以str4為基礎的StringBuilder對象,在通過append方法添加,最后通過toSting()返回一個String對象。故str7指向的還是堆內存的對象而str1指向的是常量池中的地址,兩者指向地址不相同。
第五個:intern方法使用:一個初始為空的字符串池,它由類String獨自維護。當調用 intern方法時,如果池已經包含一個等於此String對象的字符串(用equals(oject)方法確定),則返回池中的字符串。否則,將此String對象添加到池中,並返回此String對象的引用。這里str1指向常量池中的“HelloWorld”對象,str7.intern():此時常量池中已經有"HelloWorld"字符串值,所以地址指向相同
第六個:str3指向的是堆內存,而str3.intern()返回的是常量池中已有字符串“HelloWorld”的引用,故兩者指向地址不同。
面試題二:java中的String為什么設計為final類?
1. 允許String對象緩存hashCode值:在java中String類型是非常常用的,涉及到大量的增刪改查。字符串不變性保證了hashCode的唯一性,這是一種優化手段意味着不必每次都去計算hash值,這也是為什么HashMap建議用String,Integer這種不可變對象當作Key值
2. 字符串常量池需要:java中將字符串值存放在字符串常量池中,如果String對象是可變的,會產生很多邏輯錯誤,比如改變一個對象會影響到另一個獨立對象。
3. 安全性:網絡地址的url,文件路徑path通常情況啊下都是用String類型來保存,如果不是固定不變的可能產生很多安全隱患。
面視題三:有什么辦法可以改變String?
如果問了這個就很尷尬,sun公司特意設計的不可變,要強行改變只能通過反射這種騷操作
package String_test; import java.lang.reflect.Field; public class test_2 { public static void main(String[] args) throws Exception { String str = "王老吉真解渴"; System.out.println("str=: "+str); //通過反射改變獲取內部的value字符數組 Field field = String.class.getDeclaredField("value"); field.setAccessible(true); field.set(str, new char[]{'加','多','寶','也','解','渴'}); System.out.println("str=: "+str); } }
面試題四:下列代碼創建了幾個對象?
String st1 = new String(“abc”);
常量池一個“abc”對象,堆中一個"abc"對象,總共兩個。
String st1 = new String(“abc”); String st2 = new String(“abc”);
3個對象。 字符串在常量池中是唯一的,堆內存中有兩個,常量池中一個。
面試題五:談一下String,StringBuilder,StringBuffer的區別?
1.String類是字符串常量,而StringBuilder與StringBuffer是字符串變量。前者不可變后者可變
2.StringBuilder是非同步的,StringBuffer類的API都套上了一層synchronized同步修飾,所以StringBuffer適合在多線程場景使用(實際基本不用),StringBuilder類適合單線程使用,它兩用的多的就是append和insert方法
它三的適用場景可以看下知乎:https://www.zhihu.com/question/20101840
小結:
關於String類和它的成員方法,都是被final修飾的,意味着Strin類不可被繼承。String底層是采用字符數組對數據進行操作的,關於String的一切操作jdk底層都是會new一個新的String對象,在它的基礎上進行操作。所以String是不可變的。
關於String,基礎性的理論大致就這些,更多的還會考察字符串的一些算法,這部分也是需要攻克的一個難點!!
參考鏈接:https://www.cnblogs.com/xiaoxi/p/6036701.html
參考鏈接:https://blog.csdn.net/yissan/article/details/78740674
參考鏈接:https://www.zhihu.com/question/35014775/answer/60785988