為何Java中String類別是不可變的(詳解)
public final class String implements java.io.Serializable, Comparable<String>, CharSequence { /** The value is used for character storage. */ private final char value[];}
String類別的值是保存在value數組中的,並且是被private final修飾的
1、private修飾,表明外部的類是訪問不到value的,同時子類也訪問不到,當然String類不可能有子類,因為類別被final修飾了
2、final修飾,顯示value的引用是不會被改變的,而value只會在String的建構子中被初始化,而且沒有其他方法可以修改value數組中的值,保證了value的參考和值都不會改變
所以我們說String類別是不可變的。
而很多方法,如substring並不是在原來的String類別上進行操作,而是產生了新的String類別
public String substring(int beginIndex) { if (beginIndex < 0) { throw new StringIndexOutOfBoundsException(beginIndex); } int subLen = value.length - beginIndex; if (subLen < 0) { throw new StringIndexOutOfBoundsException(subLen); } return (beginIndex == 0) ? this : new String(value, beginIndex, subLen);}
為什麼String被設定為不可變的?
字串常數池
#Java有8種基本資料型別
#整數型態:byte,short,int,long。包裝類型為Byte,Short,Integer,Long
浮點類型:float、double。包裝類型為Float,Double
字元類型:char。包裝類型為Character
布林類型:boolean。包裝類型為Boolean
8種包裝類型中除了Float,Double沒有實作常數池,剩下的都實現了,當然都是透過享元模式實現的
String類別的常數池是在JVM層面實現的。
為什麼要有常數池?
常數池是為了避免頻繁的創建和銷毀物件而影響系統效能,其實現了物件的共享。
例如字串常數池,在編譯階段就把所有的字串文字放到一個常數池中。
節省記憶體空間:常數池中所有相同的字串常數合併,只佔用一個空間。
節省運行時間:比較字串時,== 比equals()快。對於兩個引用變量,只用==判斷引用是否相等,也就可以判斷實際值是否相等。
字串常數池放在哪裡?
jdk1.7之前的不討論,從jdk1.7開始,字串常數池就開始放在堆中,然後本文的所有內容都是基於jdk1.8的
下面這個程式碼還是常被問到的
String str1 = "abc"; String str2 = "abc"; String str3 = new String("abc"); String str4 = new String("abc"); // trueSystem.out.println(str1 == str2); // falseSystem.out.println(str1 == str3); // falseSystem.out.println(str3 == str4);
記憶體中的結構如下
其中常數池中存的是引用
解釋一下上面程式碼的輸出,Java中有2種建立字串物件的方式
String str1 = "abc"; String str2 = "abc"; // trueSystem.out.println(str1 == str2);
以字面上值的方式建立一個字串時,JVM首先會去字串在池中尋找是否存在"abc"這個物件
如果不存在,則在字串池中建立"abc"這個對象,然後將池中"abc"這個物件的位址賦給str1,這樣str1會指向池中"abc"這個字串物件
如果存在,則不建立任何對象,直接將池中"abc"這個物件的位址傳回,賦給str2。因為str1、str2指向同一個字串池中的"abc"對象,所以結果為true。
String str3 = new String("abc"); String str4 = new String("abc"); // falseSystem.out.println(str3 == str4);
採用new關鍵字新建一個字串物件時,JVM首先在字串池中尋找有沒有"abc"這個字串對象,
如果沒有,則先在字串池中建立一個"abc"字串對象,然後再在堆中建立一個"abc"字串對象,然後將堆中這個"abc"字串物件的地址賦給str3
如果有,則不在池中再去創建"abc"這個對象了,直接在堆中創建一個"abc"字符串對象,然後將堆中的這個"abc"對象的地址賦給str4。這樣,str4就指向了堆中所建立的這個"abc"字串物件;
因為str3和str4指向的是不同的字串對象,結果為false。
快取HashCode
String類別在被建立的時候,hashcode就被快取到hash成員變數中,因為String類別是不可變的,所以hashcode是不會改變的。這樣每次想使用hashcode的時候直接取就行了,而不用重新計算,提高了效率
public final class String implements java.io.Serializable, Comparable<String>, CharSequence { /** Cache the hash code for the string */ private int hash; // Default to 0 }
可以用作HashMap的key
## ##由於String類別不可變的特性,所以常被用作HashMap的key,如果String類別是可變的,內容改變,hashCode也會改變,當根據這個key從HashMap中取的時候有可能取不到value,或取到錯的value############線程安全性############不可變物件天生就是執行緒安全的,這樣可以避免在多執行緒環境下對String做同步操作。 ###
感謝大家的閱讀,希望大家收益多多。
本文轉自:https://blog.csdn.net/zzti_erlie/article/details/106872673
推薦教學:《java教學》
以上是為何Java中String類別是不可變的(詳解)的詳細內容。更多資訊請關注PHP中文網其他相關文章!