首頁 >Java >java教程 >為何Java中String類別是不可變的(詳解)

為何Java中String類別是不可變的(詳解)

烟雨青岚
烟雨青岚轉載
2020-06-24 13:27:146633瀏覽

為何Java中String類別是不可變的(詳解)

為何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中String類別是不可變的(詳解)
其中常數池中存的是引用

解釋一下上面程式碼的輸出,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中文網其他相關文章!

陳述:
本文轉載於:csdn.net。如有侵權,請聯絡admin@php.cn刪除