咱們在開發過程當中,特別是多種編碼格式並存的狀況下,很容易遇到亂碼問題。 假若有一個GBK編碼java文件,而後再使用-Dfile.encoding=GBK參數,寫入的文件中哪些是亂碼呢。那若是使用UFT-8編碼的java文件呢。java
public class Main { static String content = "中文"; public static void main(String[] args) throws IOException { OutputStreamWriter gbkWriter =new OutputStreamWriter(new FileOutputStream("GBK-FILE"),"GBK"); // 狀況一、二、5的結果確定是一致的 gbkWriter.write(content+"\n"); //(1) gbkWriter.write(new String(content.getBytes("GBK"),"GBK")+"\n"); // (2) gbkWriter.write(new String(content.getBytes("GBK"), "UTF-8")+"\n"); // (3) gbkWriter.write(new String(content.getBytes("UTF-8"),"GBK")+"\n"); // (4) gbkWriter.write(new String(content.getBytes("UTF-8"), "UTF-8")+"\n"); // (5) gbkWriter.flush(); gbkWriter.close(); } }
其實用一張圖就能夠清晰的歸納出從java文件編譯到輸出的過程 主要有3個地方的編碼轉換:linux
上圖①所示的位置,其實就是windows
javac -encoding xxx數組
的時候控制,若是你沒有顯示的指定編碼,那麼會根據當前操做系統的默認編碼格式進行編譯,通常windows是gbk,linux是UTF-8。若是這裏編碼指定錯了,那麼你的代碼頗有可能出現中文亂碼問題,注意是頗有可能,而不是絕對。緣由後面會說到。編譯出來的class文件統一都是UTF-8格式jvm
當class文件加載的jvm的時候,也會進行字符串編碼轉換,和前面同樣,會使用操做系統默認的編碼格式,這裏的編碼不是指class文件的編碼,而是指java文件的編碼格式,相似於指定java文件是什麼編碼格式編譯爲class文件的。就是jvm參數:編碼
java -Dfile.encoding=xxx操作系統
在java運行過程當中,字符串在內存中則是使用**Unicode(UTF-16)**進行存儲的。而UTF-8轉換爲UTF-16是很簡單的過程。當咱們標用String.getBytes()時候,則是把內存中的unicode轉換對應的字節數組。(若是沒有指定,則使用操做系統默認的編碼格式)。能夠看出,把一個字符串從編譯到內存,實際上是經歷的過程爲:code
編譯文件編碼->加載jvm編碼->unicode圖片
輸出的編碼則是在代碼中指定的。例如:內存
OutputStreamWriter gbkWriter =new OutputStreamWriter(new FileOutputStream("GBK-FILE"),"GBK");
若是理解上面的,咱們再看看文章一開始的例子。舉幾個例子作說明,其餘的狀況也就逐類旁通。
狀況1是比較好理解的,由於java文件編碼、編譯、加載都是使用GBK,加載到內存中Unicode確定也是正常的,那麼打印出來也是正常的。
在狀況1的前提下(即加載到內存中是正常的),在jvm中使用GBK解碼在編碼確定是正常的。
在狀況1的前提下,使用不一樣的解碼和編碼,確定是亂碼
當咱們使用UTF-8的格式打開文件的時候,狀況(4)是正常的,其他都是亂碼。實際上是由於先使用unicode進行轉換爲UTF-8格式的Byte數組,生成的字符串雖然亂碼和寫文件的格式都是GBK,至關於原封不動的UTF-8格式的byte數組寫到文件中,因此就會出現這個狀況
這個例子就是直至加載到內存都是正常的狀況下,在jvm內進行編碼和解碼致使亂碼的狀況
狀況1是亂碼,說明字符串加載到內存中就已是亂碼了。由於UFT-8格式使用GBK進行編碼,在生成class文件就已是亂碼了。
狀況3爲何又是正常的呢,其實這是誤打誤撞類型。我的理解的形成這個狀況的緣由有:
爲何是使用UTF-8打開狀況1不是亂碼了呢,其實和上面誤打誤撞,只不過以前發生在內存中的GBK->UTF-8換爲咱們在打開文件的時候進行的編碼轉換,狀況一、二、5結果確定一致的
和例子1不同,例子2是在生成class文件就已是亂碼的狀況。這就是前面所說的,生成class亂碼,但輸出不必定是亂碼。
一句話:必定要保證java文件、編譯、加載class文件、輸出的編碼格式一致。若是出現了亂碼,能夠從這幾個階段進行排查。