一、JVM中單個字符佔用的字節長度跟編碼方式有關,而默認編碼方式又跟平臺是一一對應的或說平臺決定了默認字符編碼方式;java
二、對於單個字符:ISO-8859-1單字節編碼,GBK雙字節編碼,UTF-8三字節編碼;所以中文平臺(中文平臺默認字符集編碼GBK)下一個中文字符佔2個字節,而英文平臺(英文平臺默認字符集編碼Cp1252(相似於ISO-8859-1))。程序員
三、getBytes()、getBytes(encoding)函數的做用是使用系統默認或者指定的字符集編碼方式,將字符串編碼成字節數組。數組
編碼方式決定字節長度;在中文平臺下,默認的字符集編碼是GBK,此時若是使用getBytes()或getBytes("GBK"),則按照GBK的編碼規則將每一箇中文字符用2個byte表示。因此咱們看到"中文"最終GBK編碼結果就是: -42 -48 -50 -60 。-42和-48表明了"中"字,而"-50"和"-60"則表明了"文"字。瀏覽器
在中文平臺下,若是指定的字符集編碼是UTF-8,那麼按照UTF-8對中文的編碼規則:每一箇中文用3個字節表示,那麼"中文"這兩個字符最終被編碼成:-28 -72 -8三、-26 -106 -121兩組。每3個字節表明一箇中文字符。服務器
在中 文平臺下,若是指定的字符集編碼是ISO-8859-1,因爲此字符集是單字節編碼,因此使用getBytes("ISO-8859-1")時,每一個字符 只取一個字節,每一個漢字只取到了一半的字符。另一半的字節丟失了。因爲這一半的字符在字符集中找不到對應的字符,因此默認使用編碼63代替,也就是?。編輯器
在英文平臺下,默認的字符集編碼是Cp1252(相似於ISO-8859-1),若是使用GBK、UTF-8進行編碼,獲得的字節數組依然是正確的(GBK4個字節,UTF-8是6個字節)。由於在JVM內部是以Unicode存儲字符串的,使用getBytes(encoding)會讓JVM進行一次Unicode到指定編碼之間的轉換。對於GBK,JVM依然會轉換成4個字節,對於UTF-8,JVM依然會轉換成6個字節。可是對於ISO-8859-1,則因爲沒法轉換(2個字節--->1個字節,截取了一半的字節),因此轉換後的結果是錯誤的。函數
在中文平臺下,默認的字符集編碼是GBK,因而content.getBytes()獲得的是什麼呢?就是下面這4個字節:編碼
byte[0] = -42 hex string = ffffffd6spa
byte[1] = -48 hex string = ffffffd0.net
byte[2] = -50 hex string = ffffffce
byte[3] = -60 hex string = ffffffc4
若是新的encoding是GBK,那麼通過解碼後,因爲一個字符用2個字節表示。因而最終的結果就是:
char[0]='中' --- byte[0] + byte[1]
char[1]='文' --- byte[2] + byte[3]
若是新的encoding是ISO-8859-1,那麼通過解碼後,因爲一個字符用1個字節 表示,因而原來本應該2個字節一塊兒解析的變成單個字節解析,每一個字節都表明了一個漢字字符的一半。這一半的字節在ISO-8859-1中找不到對應的字 符,就變成了"?"了,最終的結果:
char[0]='?' ---- byte[0]
char[1]='?' ---- byte[1]
char[2]='?' ---- byte[2]
char[3]='?' ---- byte[3]
若是新的encoding是UTF-8,那麼通過解碼後,因爲一個字符用3個字節表示,因而原來4個字節的數據沒法正常的解析成UTF-8的數據,最終的結果也是每個都變成"?"。
char[0]='?' ---- byte[0]
char[1]='?' ---- byte[1]
char[2]='?' ---- byte[2]
char[3]='?' ---- byte[3]
若是是在英文平臺下,因爲默認的編碼方式是Cp1252,因而content.getBytes()獲得的字節都是被截去一半的殘留字符,因此咱們看到在英文平臺下,不論指定的encoding是GBK、UTF-8,其結果和ISO-8859-1都是同樣的。
記 住:這個方法再次證實了String的getBytes()方法的危險性,若是咱們使用new String(str.getBytes(), encoding)對字符串進行從新編碼解碼時,咱們必定要清楚str.getBytes()方法返回的字節數組的長度、內容究竟是什麼,由於在接下來使 用新的encoding進行編碼解碼時,Java並不會自動地對字節數組進行擴展以適應新的encoding。而是按照新的編碼方法直接對該字節數組進行解析。因而結果就像上面的例子同樣,一樣是4個原始字節,有些每2個一組進行解析,有些每一個一組進行解析,有些每3個一組進行解析。其結果就只能看那種編碼方式合適了。
結論:相同的平臺下,同一個中文字符,在不一樣的編碼方式下,獲得的是徹底不一樣的字節數組。這些字節數組有多是正確的(只要該字符集支持中文),也多是徹底錯誤的(該字符集不支持中文)。
記住:不要輕易地使用或濫用String類的getBytes(encoding)方法,更要儘可能避免使用getBytes()方法。由於這個方法是平臺依賴的,在平臺不可預知的狀況下徹底可能獲得不一樣的結果。若是必定要進行字節編碼,則用戶要確保encoding的方法就是當初字符串輸入時的encoding。
———————————————————————————————————————————————————
和getBytes(encoding)不一樣,toCharArray()返回的是"天然字符"。可是這個"天然字符"的數目和內容倒是由原始的編碼方式決定的。
FileWriter是字符流輸出流,而OutputStreamWriter是字節流輸出流在中文平臺下,如 果使用FileWriter,不論你如何設置字符集都不會起做用。由於它採用的是默認的系統字符集。即使你設置了 System.setProperty("file.encoding", "ISO-8859-1"),或者在運行時給予參數-Dfile.encoding=UTF-8都不會起做用。你會發現它最終仍是都已"GB2312"或 者"GBK"的方式保存。
在中文平臺下,若是使用OutputStreamWriter,則在後臺寫入時會把字符流轉 換成字節流,此時指定的編碼字符集就起做用了。能夠看到在指定GBK、UTF-8的狀況下中文能夠正常的保存和讀取,同時文件按照咱們給定的方式保存了。 而對於ISO-8859-1則變成了?,這再次證實了採用ISO-8859-1是不能保存中文的,並且會由於中文編碼在ISO-8859-1的編碼中找不到對應的字符而默認轉換成?。
在英文平臺下,若是使用FileWriter,不論你如何設置字符集一樣都不會起做用。全部 的文件都將按照ISO-8859-1的編碼方式保存,毫無疑問地變成了?。在英文平臺下,若是使用OutputStreamWriter,則只有當咱們把 字符和文件的編碼方式正確設置爲GBK、UTF-8的狀況下,中文才能正確的保存並顯示。
經過上述的實驗證實,爲了確保在不一樣的平臺下,客戶端輸入的中文能夠被正確地解析、保存、讀取。最好的辦法就是使用OutputStreamWriter配合UTF-8編碼。若是不想使用UTF-8編碼,那麼能夠考慮使用GB2312,不建議使用GBK、GB18030。由於對於某些老式的文本編輯器,甚至不支持GBK、GB18030的編碼,可是對於GB2312則是必定支持的。由於前二者都不是國標但後者是。
關於String的getBytes(),getBytes(encoding)和new String(bytes, encoding)這三個方法,很是值得注意:A.getBytes():使用平臺默認的編碼方式(經過file.encoding屬性獲取)方式來將字 符串轉換成byte[]。獲得的是字符串最原始的字節編碼值。
B.getBytes(NAME_OF_CHARSET):使用指定的編碼方式將字符串轉換成byte[],若是想要獲得正確的字節數組,程序員必須給出正確的NAME_OF_CHARSET。不然獲得的就不會獲得正確的結果。
C.new String(bytes, encoding):如 果咱們在客戶端使用UTF-8編碼的JSP頁面發出請求,瀏覽器編碼後的UTF-8字節會以ISO-8859-1的形式傳遞到服務器端。因此要獲得經 HTTP協議傳輸的原始字節,咱們須要先調用getBytes("ISO-8859-1")獲得原始的字節,但因爲咱們客戶端的原始編碼是UTF-8,如 果繼續按照ISO-8859-1解碼,那麼獲得的將不是一箇中文字符,而是3個亂碼的字符。因此咱們須要再次調用new String(bytes,"UTF-8"),將字節數組按照UTF-8的格式,每3個一組進行解碼,才能還原爲客戶端的原始字符。
D.String的getBytes()、 getBytes(NAME_OF_CHARSET)方法都是比較微妙的方法,原則上:傳輸時採用的是什麼編碼,咱們就須要按照這種編碼獲得字節。new String(bytes, NAME_OF_CHARSET)則更加須要當心,原則上:客戶端採用的是什麼編碼,那麼這裏的NAME_OF_CHARSET就必須和客戶端保持一致。例如JSP頁面是GBK,那麼咱們接收頁面傳遞而來的參數時就必須使用new String(parameter.getBytes("ISO-8859-1"), "GBK");若是使用了錯誤 的解碼方式,如使用了UTF-8,那麼獲得的頗有可能就是亂碼了。也就是說:GBK--->ISO-8859-1--->GBK、UTF- 8--->ISO-8859-1--->UTF-8的轉換過程是沒有問題的。可是GBK--->ISO- 8859-1--->UTF-八、UTF-8--->ISO-8859-1--->GBK的字節直接轉碼則可能致使亂碼,須要另外的轉 換過程。
記住:謹 慎地使用getBytes(NAME_OF_CHARSET)和new String(bytes, NAME_OF_CHARSET),除非你很清楚的知道原始的字符編碼和傳輸協議使用的編碼。推薦使用基於服務器的配置、過濾器設置 request/response的characterEncoding、content type屬性。還有就是JSP頁面的pageEncoding屬性、HTML meta元素的content type屬性。儘可能避免頻繁的在代碼中進行字符串轉碼,即下降了效率又增長了風險。