溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點(diǎn)擊 登錄注冊 即表示同意《億速云用戶服務(wù)條款》

為什么Java中String類設(shè)置完不能改

發(fā)布時(shí)間:2020-06-24 14:06:09 來源:億速云 閱讀:174 作者:元一 欄目:編程語言

為什么Java中String類設(shè)置完不能改?可能很多人都不太了解,為了讓大家更加了解,小編給大家總結(jié)了以下內(nèi)容,希望大家根據(jù)這篇文章可以有所收獲。

String就是java等編程語言中的字符串,用雙引號引起來的幾個(gè)字符.如"Abc","一天".與字符不同的是它指的是一串字符或者一類標(biāo)識符。String類是不可變(final)的,對String類的任何改變,都是返回一個(gè)新的String類對象.這樣的話把String類的引用傳遞給一個(gè)方法,該方法對String的任何改變,對原引用指向的對象沒有任何影響,這一點(diǎn)和基本數(shù)據(jù)類型相似。

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數(shù)組中的,并且是被private final修飾的

1、private修飾,表明外部的類是訪問不到value的,同時(shí)子類也訪問不到,當(dāng)然String類不可能有子類,因?yàn)轭惐籪inal修飾了
2、final修飾,表明value的引用是不會被改變的,而value只會在String的構(gòu)造函數(shù)中被初始化,而且并沒有其他方法可以修改value數(shù)組中的值,保證了value的引用和值都不會發(fā)生變化

所以我們說String類是不可變的。

而很多方法,如substring并不是在原來的String類上進(jìn)行操作,而是生成了新的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);}

字符串常量池

Java有8種基本數(shù)據(jù)類型

整數(shù)類型:byte,short,int,long。包裝類型為Byte,Short,Integer,Long
浮點(diǎn)類型:float、double。包裝類型為Float,Double
字符類型:char。包裝類型為Character
布爾類型:boolean。包裝類型為Boolean

8種包裝類型中除了Float,Double沒有實(shí)現(xiàn)常量池,剩下的都實(shí)現(xiàn)了,當(dāng)然都是通過享元模式實(shí)現(xiàn)的

String類的常量池是在JVM層面實(shí)現(xiàn)的。

為什么要有常量池?

常量池是為了避免頻繁的創(chuàng)建和銷毀對象而影響系統(tǒng)性能,其實(shí)現(xiàn)了對象的共享。
例如字符串常量池,在編譯階段就把所有的字符串文字放到一個(gè)常量池中。

  • 節(jié)省內(nèi)存空間:常量池中所有相同的字符串常量被合并,只占用一個(gè)空間。

  • 節(jié)省運(yùn)行時(shí)間:比較字符串時(shí),== 比equals()快。對于兩個(gè)引用變量,只用==判斷引用是否相等,也就可以判斷實(shí)際值是否相等。

字符串常量池放在哪?

jdk1.7之前的不討論,從jdk1.7開始,字符串常量池就開始放在堆中,然后本文的所有內(nèi)容都是基于jdk1.8的

下面這個(gè)代碼還是經(jīng)常被問到的

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);

內(nèi)存中的結(jié)構(gòu)如下
為什么Java中String類設(shè)置完不能改
其中常量池中存的是引用

解釋一下上面代碼的輸出,Java中有2種創(chuàng)建字符串對象的方式

String str1 = "abc";
String str2 = "abc";
// trueSystem.out.println(str1 == str2);

采用字面值的方式創(chuàng)建一個(gè)字符串時(shí),JVM首先會去字符串池中查找是否存在"abc"這個(gè)對象

如果不存在,則在字符串池中創(chuàng)建"abc"這個(gè)對象,然后將池中"abc"這個(gè)對象的地址賦給str1,這樣str1會指向池中"abc"這個(gè)字符串對象

如果存在,則不創(chuàng)建任何對象,直接將池中"abc"這個(gè)對象的地址返回,賦給str2。因?yàn)閟tr1、str2指向同一個(gè)字符串池中的"abc"對象,所以結(jié)果為true。

String str3 = new String("abc");
String str4 = new String("abc");
// falseSystem.out.println(str3 == str4);

采用new關(guān)鍵字新建一個(gè)字符串對象時(shí),JVM首先在字符串池中查找有沒有"abc"這個(gè)字符串對象,

如果沒有,則首先在字符串池中創(chuàng)建一個(gè)"abc"字符串對象,然后再在堆中創(chuàng)建一個(gè)"abc"字符串對象,然后將堆中這個(gè)"abc"字符串對象的地址賦給str3

如果有,則不在池中再去創(chuàng)建"abc"這個(gè)對象了,直接在堆中創(chuàng)建一個(gè)"abc"字符串對象,然后將堆中的這個(gè)"abc"對象的地址賦給str4。這樣,str4就指向了堆中創(chuàng)建的這個(gè)"abc"字符串對象;

因?yàn)閟tr3和str4指向的是不同的字符串對象,結(jié)果為false。

緩存HashCode

String類在被創(chuàng)建的時(shí)候,hashcode就被緩存到hash成員變量中,因?yàn)镾tring類是不可變的,所以hashcode是不會改變的。這樣每次想使用hashcode的時(shí)候直接取就行了,而不用重新計(jì)算,提高了效率

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類不可變的特性,所以經(jīng)常被用作HashMap的key,如果String類是可變的,內(nèi)容改變,hashCode也會改變,當(dāng)根據(jù)這個(gè)key從HashMap中取的時(shí)候有可能取不到value,或者取到錯(cuò)的value

線程安全

不可變對象天生就是線程安全的,這樣可以避免在多線程環(huán)境下對String做同步操作。

看完上述內(nèi)容,你們對Java中String類有進(jìn)一步的了解嗎?如果還想了解更多相關(guān)內(nèi)容,歡迎關(guān)注億速云行業(yè)資訊頻道,感謝各位的閱讀。

向AI問一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI