1. 程式人生 > >Java 中的 char型別和字串編碼解析

Java 中的 char型別和字串編碼解析

  首先,我配上一張圖,可以很好的解釋問題:

    這張圖表明,不論java檔案是什麼字元編碼的,經過編譯器編譯後,字元在class檔案中都會變成UTF-8編碼,實際上是一種modified UTF-8,modified utf-8是java對utf-8作了修改的版本。

   後面,當JVM把這個class檔案載入記憶體後,就會把字元轉換為UTF-16編碼存放在記憶體中。因此,在執行時,記憶體中字串的編碼都是用UTF-16方式編碼的。

下面,說正事。

   最初,Unicode字符集設想用兩個位元組就能表示所有的字元了,所以Java也把char型別設定為2位元組的大小。但是,後來東亞文字的加入使得兩個位元組不足以表示所有的字元。於是, Unicode開始擴充套件,把編碼範圍變為0x0到0x10FFFF。Java把這個編碼值稱為程式碼點。Unicode的程式碼點可以分成17個程式碼級別(code plane)。第一個程式碼級別稱為基本的多語言級別,程式碼點從U+0000到U+FFFF,這包括了經典的Unicode程式碼。其餘的是16個附加級別,程式碼點從U+10000到U+10FFFF,其中包括了一些輔助字元(supplementary character)。其實就是每個級別分別佔據著65536個位置,即16位。

   Java程式在執行時,即JVM內部,對於字符采用UTF-16編碼。K這個編碼方式採用不同的編碼表示所有的Unicode程式碼點。在基本的多語言級別中,每個字元用16位表示,稱為一個程式碼單元。所以說,一個char型別就是一個程式碼單元。而對於輔助字元,Java採用一對連續的程式碼單元來進行編碼,即32位。因為基本的多語言級別中有2048個位置是空閒的(U+D800到U+DFFF),所以用來編碼輔助字元的程式碼單元的值必然要落在這個空閒區間中,通常稱為替代區域(surrogate area)。其中,U+D800~U+DBFF用於第一個程式碼單元,U+DC00~U+DFFF用於第二個程式碼單元。這樣做,我們就可以迅速判斷這是用一個程式碼單元還是兩個,還能迅速判斷是第幾個程式碼單元。至於,UTF-16具體是如何用兩個程式碼單元來編碼一個程式碼點的,這裡我就不說了,自己百度吧。

   說到這裡,就可以知道java中的char型別其實是比較底層的,有時候一個char型別不一定就表示一個字元,對於比較生僻的字元要兩個才行。所以,有時候String的length返回值不一定與能看到的字元數相等,因為length方法返回的是char型別的數量。

   最後再講一下JVM的預設編碼,即defaultCharset。這個不是指UTF-16,JVM內部的字串永遠都是UTF-16編碼的,而是指把JVM內部的UTF-16字串轉換成Byte陣列時或輸出時的預設轉換編碼,即要把字串從UTF-16編碼轉換為這個預設編碼。我覺得也可以理解為,當一個方法需要一個指定字元編碼的引數時,如果預設不寫就會用這個預設編碼。這個預設編碼通常就是作業系統的預設編碼,中文Windows通常就是GBK編碼。當然,我們也可以改,就是在執行程式的時候加上引數-Dfile.encoding=xx來修改這個預設編碼。