發(fā)布時(shí)間:2024-01-24閱讀(10)

在學(xué)習(xí)和開(kāi)發(fā)過(guò)程中,我們經(jīng)常會(huì)討論 short ,int 和 long 這些基本數(shù)據(jù)類型的取值范圍,但是對(duì)于 String 類型我們好像很少注意它的“取值范圍”。那么對(duì)于 String 類型,它到底有沒(méi)有長(zhǎng)度限制呢?
其實(shí) String 類型的對(duì)象,他們是有長(zhǎng)度限制的, String 對(duì)象并不能“存儲(chǔ)”無(wú)限長(zhǎng)度的字符串。關(guān)于 String 的長(zhǎng)度限制要從編譯時(shí)限制和運(yùn)行時(shí)限制兩方面考慮。
編譯期限制有JVM虛擬機(jī)相關(guān)知識(shí)的同學(xué)肯定知道,下面定義的字符串常量“自由之路”會(huì)被放入方法區(qū)的常量池中。
CopyString s = "自由之路";System.out.println(s);
Stirng 長(zhǎng)度之所以會(huì)受限制,是因JVM規(guī)范對(duì)常量池有所限制。常量池中的每一種數(shù)據(jù)項(xiàng)都有自己的類型。Java中的UTF-8編碼的Unicode字符串在常量池中以CONSTANT_Utf8類型表示。
CONSTANT_Utf8的數(shù)據(jù)結(jié)構(gòu)如下:
CopyCONSTANT_Utf8_info { u1 tag; u2 length; u1 bytes[length];}
我們重點(diǎn)關(guān)注下長(zhǎng)度為 length 的那個(gè)bytes數(shù)組,這個(gè)數(shù)組就是真正存儲(chǔ)常量數(shù)據(jù)的地方,而 length 就是數(shù)組可以存儲(chǔ)的最大字節(jié)數(shù)。length 的類型是u2,u2是無(wú)符號(hào)的16位整數(shù),因此理論上允許的的最大長(zhǎng)度是2^16-1=65535。所以上面byte數(shù)組的最大長(zhǎng)度可以是65535。
Copy//65535個(gè)d,編譯報(bào)錯(cuò)String s = "dd..dd";//65534個(gè)d,編譯通過(guò)String s1 = "dd..d";
上面的列子中長(zhǎng)度為65535的字符串s還是編譯失敗了,但是長(zhǎng)度為65534的字符串 s1 編譯是成功的。這個(gè)好像和我們剛剛的結(jié)論不符合。
其實(shí),這是Javac編譯器的額外限制。在Javac的源代碼中可以找到以下代碼:
Copyprivate void checkStringConstant(DiagnosticPosition var1, Object var2) { if (this.nerrs == 0 && var2 != null && var2 instanceof String && ((String)var2).length() >= 65535) { this.log.error(var1, "limit.string", new Object[0]); this.nerrs; }}
代碼中可以看出,當(dāng)參數(shù)類型為String,并且長(zhǎng)度大于等于65535的時(shí)候,就會(huì)導(dǎo)致編譯失敗。
這里需要重點(diǎn)強(qiáng)調(diào)下的是:String 的限制并不是對(duì)字符串長(zhǎng)度的限制,而是對(duì)字符串底層存儲(chǔ)的限制。這句話可能比較抽象,下面舉個(gè)例子就清楚了。
Java中的字符常量都是使用UTF8編碼的,UTF8編碼使用1~4個(gè)字節(jié)來(lái)表示具體的Unicode字符。所以有的字符占用一個(gè)字節(jié),而我們平時(shí)所用的大部分中文都需要3個(gè)字節(jié)來(lái)存儲(chǔ)。
Copy//65534個(gè)字母,編譯通過(guò)String s1 = "dd..d";//21845個(gè)中文”自“,編譯通過(guò)String s2 = "自自...自";//一個(gè)英文字母d加上21845個(gè)中文”自“,編譯失敗String s3 = "d自自...自";
對(duì)于s1,一個(gè)字母d的UTF8編碼占用一個(gè)字節(jié),65534字母占用65534個(gè)字節(jié),長(zhǎng)度是65534,也沒(méi)超過(guò)Javac的限制,所以可以編譯通過(guò)。
對(duì)于s2,一個(gè)中文占用3個(gè)字節(jié),21845個(gè)正好占用65535個(gè)字節(jié),而且字符串長(zhǎng)度是21845,并沒(méi)有超過(guò)javac對(duì)長(zhǎng)度的限制,所以可以編譯通過(guò)。
對(duì)于s3,一個(gè)英文字母d加上21845個(gè)中文“自”占用65535個(gè)字節(jié),超過(guò)了最常限制,編譯失敗。
運(yùn)行時(shí)限制String 運(yùn)行時(shí)的限制主要體現(xiàn)在 String 的構(gòu)造函數(shù)上。下面是 String 的一個(gè)構(gòu)造函數(shù):
Copypublic String(char value[], int offset, int count) { ...}
上面的count值就是字符串的最大長(zhǎng)度。在Java中,int的最大長(zhǎng)度是2^31-1。所以在運(yùn)行時(shí),String 的最大長(zhǎng)度是2^31-1。
但是這個(gè)也是理論上的長(zhǎng)度,實(shí)際的長(zhǎng)度還要看你JVM的內(nèi)存。我們來(lái)看下,最大的字符串會(huì)占用多大的內(nèi)存。
Copy(2^31-1)*2*16/8/1024/1024/1024 = 4GB
所以在最壞的情況下,一個(gè)最大的字符串要占用4GB的內(nèi)存。如果你的虛擬機(jī)不能分配這么多內(nèi)存的話,會(huì)直接報(bào)錯(cuò)的。
JDK9以后對(duì)String的存儲(chǔ)進(jìn)行了優(yōu)化。底層不再使用char數(shù)組存儲(chǔ)字符串,而是使用byte數(shù)組。對(duì)于LATIN1字符的字符串可以節(jié)省一倍的內(nèi)存空間。
簡(jiǎn)單總結(jié)String 的長(zhǎng)度是有限制的。
編譯期的限制:字符串的UTF8編碼值的字節(jié)數(shù)不能超過(guò)65535,字符串的長(zhǎng)度不能超過(guò)65534;
運(yùn)行時(shí)限制:字符串的長(zhǎng)度不能超過(guò)2^31-1,占用的內(nèi)存數(shù)不能超過(guò)虛擬機(jī)能夠提供的最大值。
作者:程序員自由之路
來(lái)源:https://www.cnblogs.com/54chensongxia/p/13640352.html
歡迎分享轉(zhuǎn)載→http://m.avcorse.com/read-222469.html
Copyright ? 2024 有趣生活 All Rights Reserve吉ICP備19000289號(hào)-5 TXT地圖HTML地圖XML地圖