- 資訊首頁(yè) > 開(kāi)發(fā)技術(shù) >
- Java String保存字符串的機制
Java 中的 Unicode 字符串會(huì )按照 Latin1(所有的字符都小于 0xFF 時(shí))或者 UTF16 的編碼格式保存在 String 中,保存為 byte 數組:
private final byte[] value;
通常所說(shuō)的 Immutable 都是指 final bytes 在 String 初始化后就不會(huì )修改,所有字符串的相關(guān)操作都是不會(huì )修改原數組而是創(chuàng )建新的副本。
但是數組元素理論上是可以修改的,比如下面通過(guò)反射的方式,將字符串常量 abc 修改為 Abc:
public static void main(String[] args) { setFirstValueToA("abc"); String replaced = new String("abc"); System.out.println(replaced); // Abc } private static void setFirstValueToA(String str) { Class<String> stringClass = String.class; try { Field value = stringClass.getDeclaredField("value"); value.setAccessible(true); byte[] bytes = (byte[]) value.get(str); bytes[0] = 0x41; // A } catch (NoSuchFieldException | IllegalAccessException e) { e.printStackTrace(); } }
通過(guò)如下代碼測試幾個(gè)字符串數組:
public static void main(String[] args) { printString("abc"); printString("中文"); printString("abc中文"); printString("abc"); } private static void printString(String str) { System.out.println("======>" + str); // return the UTF-16 char[] size System.out.println("length: " + str.length()); // Use default Encoding (UTF-8) System.out.println("getBytes: " + str.getBytes().length); // Convert UTF-16 char[] to char System.out.println("codePointCount: " + str.codePointCount(0, str.length())); // Get the UTF-16 char[] System.out.println("toCharArray: " + str.toCharArray().length); // The UTF-16 char[] to bytes System.out.println("internal value: " + getStringInternalValueLength(str)); }
結果如下:
首先解釋下 String 的 value 字段計算方式:
Unicode 是 Coded Character Set,將幾乎所有的人類(lèi)文字映射到 code point 符號,通常格式為 U+xxxx,xxxx 為 16 進(jìn)制整數,表達范圍為 U+0000~U+10FFFF。code point 符號是文字的規范化標記,但是實(shí)際保存時(shí)肯定還是要保存為字節數組的。這些不同的保存方式就是 Character Encoding,比如 UTF-8,還有 Java String 內部采用的 UTF-16。
UTF-16 是一種將 Unicode code point 表達成字符數組的編碼方式,對于 U+0000~U+FFFF,直接按照 2 個(gè)字節保存(細分的話(huà)還有大端字節序和小端字節序的區別);對于 U+10000~U+10FFFF,會(huì )先轉化為一對 U+D800~U+DFFF 范圍內的 code point(surrogate pair),再將這兩個(gè) code point 按照前面的規則保存。之所以選擇這個(gè)范圍,是因為這個(gè) Unicode 區間還沒(méi)有被分配有效的字符,因此可以和前面的規則區分。
“中文”這兩個(gè)漢字的 Unicode code point 非別為 U+4E2d、U+6587,大于 0xFF,所以保存 byte 長(cháng)度為 4;"abc中文" 中存在不滿(mǎn)足條件的字符,所以全部用 UTF-16 保存,它們都是 2 個(gè) byte 的,所以長(cháng)度為 10。
“☺” 的 Unicode code point 為 U+1F60A,根據 UTF-16 規范,U+10000~U+10FFFF 需要轉化為 surrogate pair 之后再保存成 byte, 轉換后為 U+D83D、U+DE0A,因此 "abc" 的字節長(cháng)度為 10。
Java 中 char 的大小為 2 個(gè)字節,剛好可以表示一個(gè) U+0000~U+FFFF 的 Unicode 符號。
Latin1 編碼時(shí),char 數組為 byte 數組的填充,高字節為 0;UTF-16 編碼時(shí),相當于轉化過(guò) surrogate pair 后的 Unicode 編碼數組,其中 0xD800~0xDFFF 范圍內的為 surrogate 字符。
“abc” 時(shí)為 Latin1 編碼,所以 char 數組大小等于 bytes 數組;“abc中文” 時(shí)為 UTF-16 編碼,所以 char 數組大小等于 bytes 數組的一半。
toCharArray 方法將轉化后的 surrogate pair 也算在內,因此實(shí)際長(cháng)度可能大于字符長(cháng)度。而 codePointCount 就能去除 surrogate pair 的影響,返回初始的字符長(cháng)度,它會(huì )將連續兩個(gè) surrogate pair 只計數一次。
該方法就是 toCharArray 數組的長(cháng)度,受到 surrogate pair 的影響,可能大于字符長(cháng)度。
String 內部是通過(guò) UTF-16 編碼保存的字節數組,當通過(guò) getBytes 方法返回時(shí),是需要指定 Encoding 的,默認采用 UTF-8,因此會(huì )將 UTF-16 的字節數組轉化為 UTF-8 的字節數組,每個(gè) Unicode 符號在 UTF-8 編碼后長(cháng)度為 1~4 字節。
System.out.println("abc".getBytes(UTF_8).length); // 3 System.out.println("中".getBytes(UTF_8).length); // 3 System.out.println("文".getBytes(UTF_8).length); // 3 System.out.println("".getBytes(UTF_8).length); // 4
到此這篇關(guān)于Java String保存字符串的機制的文章就介紹到這了,更多相關(guān)Java String保存字符串內容請搜索腳本之家以前的文章或繼續瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
免責聲明:本站發(fā)布的內容(圖片、視頻和文字)以原創(chuàng )、來(lái)自互聯(lián)網(wǎng)轉載和分享為主,文章觀(guān)點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權請聯(lián)系QQ:712375056 進(jìn)行舉報,并提供相關(guān)證據,一經(jīng)查實(shí),將立刻刪除涉嫌侵權內容。
Copyright ? 2009-2021 56dr.com. All Rights Reserved. 特網(wǎng)科技 特網(wǎng)云 版權所有 珠海市特網(wǎng)科技有限公司 粵ICP備16109289號
域名注冊服務(wù)機構:阿里云計算有限公司(萬(wàn)網(wǎng)) 域名服務(wù)機構:煙臺帝思普網(wǎng)絡(luò )科技有限公司(DNSPod) CDN服務(wù):阿里云計算有限公司 中國互聯(lián)網(wǎng)舉報中心 增值電信業(yè)務(wù)經(jīng)營(yíng)許可證B2
建議您使用Chrome、Firefox、Edge、IE10及以上版本和360等主流瀏覽器瀏覽本網(wǎng)站