java编码详解

举个例子

咱们在开发过程当中,特别是多种编码格式并存的状况下,很容易遇到乱码问题。 假若有一个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编译到输出

其实用一张图就能够清晰的归纳出从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,
  • javac -encoding GBK
  • java -Dfile.encoding=GBK 那么使用GBK编码查看输出文件
  • (1)正常
  • (2)正常
  • (3)乱码
  • (4)乱码
  • (5)正常

状况(1)

状况1是比较好理解的,由于java文件编码、编译、加载都是使用GBK,加载到内存中Unicode确定也是正常的,那么打印出来也是正常的。

状况(2)和状况(5)

在状况1的前提下(即加载到内存中是正常的),在jvm中使用GBK解码在编码确定是正常的。

状况(3)和状况(4)

在状况1的前提下,使用不一样的解码和编码,确定是乱码

特殊状况

当咱们使用UTF-8的格式打开文件的时候,状况(4)是正常的,其他都是乱码。实际上是由于先使用unicode进行转换为UTF-8格式的Byte数组,生成的字符串虽然乱码和写文件的格式都是GBK,至关于原封不动的UTF-8格式的byte数组写到文件中,因此就会出现这个状况

小节

这个例子就是直至加载到内存都是正常的状况下,在jvm内进行编码和解码致使乱码的状况

例子2

  • java文件编码:UTF-8,
  • javac -encoding GBK
  • java -Dfile.encoding=UTF-8

那么使用GBK编码查看输出文件

  • (1)乱码
  • (2)乱码
  • (3)正常
  • (4)乱码
  • (5)乱码

状况(1)

状况1是乱码,说明字符串加载到内存中就已是乱码了。由于UFT-8格式使用GBK进行编码,在生成class文件就已是乱码了。

状况(3)

状况3为何又是正常的呢,其实这是误打误撞类型。我的理解的形成这个状况的缘由有:

  1. java文件的编码正好和jvm加载文件编码格式是同样的
  2. javac过程,至关于一个UTF-8->GBK格式转换,而content.getBytes("GBK"), "UTF-8")又至关于GBK->UTF-8的转换,两次转换正好相互抵消。

使用UTF-8编码查看输出文件

  • (1)正常
  • (2)正常
  • (3)乱码
  • (4)乱码
  • (5)正常

为何是使用UTF-8打开状况1不是乱码了呢,其实和上面误打误撞,只不过以前发生在内存中的GBK->UTF-8换为咱们在打开文件的时候进行的编码转换,状况一、二、5结果确定一致的

小结

和例子1不同,例子2是在生成class文件就已是乱码的状况。这就是前面所说的,生成class乱码,但输出不必定是乱码。

总结

一句话:必定要保证java文件、编译、加载class文件、输出的编码格式一致。若是出现了乱码,能够从这几个阶段进行排查。

相关文章
相关标签/搜索