举个例子

我们在开发过程中,特别是多种编码格式并存的情况下,很容易遇到乱码问题。 假如有一个GBK编码java文件,然后再使用-Dfile.encoding=GBK参数,写入的文件中哪些是乱码呢。那如果使用UFT-8编码的java文件呢。

public class Main {
static String content = "中文"; public static void main(String[] args) throws IOException {
OutputStreamWriter gbkWriter =new OutputStreamWriter(new FileOutputStream("GBK-FILE"),"GBK"); // 情况1、2、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个地方的编码转换:

编译过程

上图①所示的位置,其实就是

javac -encoding xxx

的时候控制,如果你没有显示的指定编码,那么会根据当前操作系统的默认编码格式进行编译,一般windows是gbk,linux是UTF-8。如果这里编码指定错了,那么你的代码很有可能出现中文乱码问题,注意是很有可能,而不是绝对。原因后面会说到。编译出来的class文件统一都是UTF-8格式

运行加载

当class文件加载的jvm的时候,也会进行字符串编码转换,和前面一样,会使用操作系统默认的编码格式,这里的编码不是指class文件的编码,而是指java文件的编码格式,类似于指定java文件是什么编码格式编译为class文件的。就是jvm参数:

java -Dfile.encoding=xxx

在java运行过程中,字符串在内存中则是使用Unicode(UTF-16)进行存储的。而UTF-8转换为UTF-16是很简单的过程。当我们标用String.getBytes()时候,则是把内存中的unicode转换对应的字节数组。(如果没有指定,则使用操作系统默认的编码格式)。可以看出,把一个字符串从编译到内存,其实是经历的过程为:

编译文件编码->加载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换为我们在打开文件的时候进行的编码转换,情况1、2、5结果肯定一致的

小结

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

总结

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

 

java编码详解的更多相关文章

  1. C++调用JAVA方法详解

    C++调用JAVA方法详解          博客分类: 本文主要参考http://tech.ccidnet.com/art/1081/20050413/237901_1.html 上的文章. C++ ...

  2. java 乱码详解_jsp中pageEncoding、charset=UTF -8"、request.setCharacterEncoding("UTF-8")

    http://blog.csdn.net/qinysong/article/details/1179480 java 乱码详解__jsp中pageEncoding.charset=UTF -8&quo ...

  3. Python2.7字符编码详解

    目录 Python2.7字符编码详解 声明 一. 字符编码基础 1.1 抽象字符清单(ACR) 1.2 已编码字符集(CCS) 1.3 字符编码格式(CEF) 1.3.1 ASCII(初创) 1.3. ...

  4. Java IO 详解

    Java IO 详解 初学java,一直搞不懂java里面的io关系,在网上找了很多大多都是给个结构图草草描述也看的不是很懂.而且没有结合到java7 的最新技术,所以自己来整理一下,有错的话请指正, ...

  5. java关键字(详解)

    目录 1. 基本类型 1) boolean 布尔型 2) byte 字节型 3) char 字符型 4) double 双精度 5) float 浮点 6) int 整型 7) long 长整型 8) ...

  6. 转1:Python字符编码详解

    Python27字符编码详解 声明 一 字符编码基础 1 抽象字符清单ACR 2 已编码字符集CCS 3 字符编码格式CEF 31 ASCII初创 311 ASCII 312 EASCII 32 MB ...

  7. 数据结构图文解析之:哈夫曼树与哈夫曼编码详解及C++模板实现

    0. 数据结构图文解析系列 数据结构系列文章 数据结构图文解析之:数组.单链表.双链表介绍及C++模板实现 数据结构图文解析之:栈的简介及C++模板实现 数据结构图文解析之:队列详解与C++模板实现 ...

  8. Java内部类详解

    Java内部类详解 说起内部类这个词,想必很多人都不陌生,但是又会觉得不熟悉.原因是平时编写代码时可能用到的场景不多,用得最多的是在有事件监听的情况下,并且即使用到也很少去总结内部类的用法.今天我们就 ...

  9. 黑马----JAVA迭代器详解

    JAVA迭代器详解 1.Interable.Iterator和ListIterator 1)迭代器生成接口Interable,用于生成一个具体迭代器 public interface Iterable ...

随机推荐

  1. 智能指针剖析(上)std::auto_ptr与boost::scoped_ptr

    1. 引入 C++语言中的动态内存分配没有自动回收机制,动态开辟的空间需要用户自己来维护,在出函数作用域或者程序正常退出前必须释放掉. 即程序员每次 new 出来的内存都要手动 delete,否则会造 ...

  2. [刷题]算法竞赛入门经典(第2版) 5-16/UVa212 - Use of Hospital Facilities

    题意:模拟患者做手术. 其条件为:医院有Nop个手术室.准备手术室要Mop分钟,另有Nre个恢复用的床.准备每张床要Mre分钟,早上Ts点整医院开张,从手术室手术完毕转移到回复床要Mtr分钟.现在医院 ...

  3. 关于php调用.net的web service 踩过的坑

    从前一阵开始,公司要和对方做web service对接.由于对方使用.net语言,而我方使用php.本来经理是要求我们也用.net写web service的服务端.而我上学时学的.net全忘了... ...

  4. MySQL的SELECT ...for update

    最近的项目中,因为涉及到Mysql数据中乐观锁和悲观锁的使用,所以结合项目和网上的知识点对乐观锁和悲观锁的知识进行总结. 悲观锁介绍 悲观锁是对数据被的修改持悲观态度(认为数据在被修改的时候一定会存在 ...

  5. 实现ThreadFactory接口生成自定义的线程给Fork/Join框架

    Fork/Join框架是Java7中最有趣的特征之一.它是Executor和ExecutorService接口的一个实现,允许你执行Callable和Runnable任务而不用管理这些执行线程.这个执 ...

  6. 基于spark和sparkstreaming的word2vec

    概述 Word2vec是一款由谷歌发布开源的自然语言处理算法,其目的是把words转换成vectors,从而可以用数学的方法来分析words之间的关系.Spark其该算法进行了封装,并在mllib中实 ...

  7. linux通配符与正则表达式

    通配符   *  任意字符,可重复多次     ? 任意字符,重复一次     [] 代表一个字符 举例: [a,b,c] 表示abc中任意一个 通配符的作用是用来匹配文件名的 正则表达式 正则表达式 ...

  8. .Net程序员学用Oracle系列(29):PLSQL 之批量应用和系统包

    1.批量数据操作 1.1.批量生成数据 1.2.批量插入数据 2.批量生成脚本 3.生成数据字典 4.常见系统包 4.1.DBMS_OUTPUT 4.2.DBMS_RANDOM 4.3.其它系统包及常 ...

  9. WannaflyUnion每日一题

    ---恢复内容开始--- 1. http://www.spoj.com/problems/KAOS/ 题意:给定n个字符串,统计字符串(s1, s2)的对数,使得s1的字典序比s2的字典序要大,s1反 ...

  10. .Net程序员学用Oracle系列(30):零碎补充、最后总结(The End)

    1.同义词 2.Flashback 技术 3.连接字符串的写法 4.转义字符 & 特殊运算符 5.文件类型 6.查看参数 & 修改参数 7.AWR 工具 8.学习方法 & 学习 ...