zoukankan      html  css  js  c++  java
  • 如何理解java采用Unicode编码

    http://blog.csdn.net/gjb724332682/article/details/43229563

    Java中字符仅以一种形式存在,那就是Unicode。由于java采用unicode编码,char 在java中占2个字节。2个字节(16位)来表示一个字符。
    这里的Java中是指在JVM中、在内存中、在代码里声明的每一个char、String类型的变量中。
    例如:

     

    [java] view plain copy
     
    1. public static void main(String[] args) {  
    2.         System.out.println(System.getProperty("file.encoding"));//输出UTF-8  
    3.         char han = '永';  
    4.         System.out.format("%x", (short) han);//输出6c38  
    5.         char han1 = 0x6c38;  
    6.         System.out.println(han1);//输出永  
    7.     }  

    也就是说,只要我们正确地读入了汉字“永”字,那么它在内存中的表示形式一定是0x6c38,没有其他任何值能替代这个字。
    JVM的折中约定使得一个字符分为两部分:JVM内部和OS的文件系统。在JVM内部,统一使用Unicode表示,当这个字符被从JVM内部移到外部(即保存为文件系统中的一个文件的内容时),就进行了编码转换,使用了具体的编码方案。因此可以说所有的编码转换只发生在边界的地方,JVM和OS的交界处,也就是各种输入/输出流(或者Reader,Writer类)起作用的地方。
    所有的I/O基本上可以分为两大阵营:面向字符的输入/输出流;面向字节的输入/输出流。如果面向字节,那么这类工作要保证系统中的文件二进制内容和读入JVM内部的二进制内容一致,不能变换任何0和1的顺序。这种输入/输出方式很适合诸如视频文件或者音频文件,因为不需要变换任何文件内容。而面向字符的I/O是指希望系统中的文件的字符和读入内存的“字符”要一致。例如:我们的中文版XP系统上有一个GBK的文本文件,其中有一个“永”字,我们不关心这个字的GBK编码是什么,只希望在使用面向字符的I/O把它读入内存并保存在一个char型变量中时,I/O系统不要直接把“永”字的GBK编码放到这个字符(char)型变量中,我们不关心这个char型变量具体的二进制内容到底是多少,只希望这个字符读进来之后仍然是“永”字。
    从这个意义上可以看出,面向字符的I/O类,也就是Reader和Writer类,实际上隐式做了编码转换,在输出时,将内存中的Unicode字符使用系统默认编码方式进行了编码,而在输入时,将文件系统中已经编码过的字符使用默认编码方案进行了还原。这里要注意,Reader和Writer只会使用这个默认的编码来做转换,而不能为一个Reader和Writer指定转换时使用的编码。这也意味着,如果使用中文版WindowsXP系统,其中存放了一个UTF8编码的文件,当采用Reader类来读入的时候,它还会用GBK来转换,转换后的内容当然不对。这其实是一种傻瓜式的功能提供方式,对大多数初级用户(以及不需要跨平台的高级用户,windows一般采用GBK,linux一般采用UTF8)来说反而是一件好事。
    如果用到GBK编码以外的文件,就必须采用编码转换:一个字符与字节之间的转换。因此,Java的I/O系统中能够指定转换编码的地方,也就是在字符与字节转换的地方,那就是InputStremReader与OutputStreamWriter。这两个类是字节流和字符流的适配器类,它们承担编码转换的任务。
    既然java是用unicode来编码字符,"我"这个中文字符的unicode就是2个字节。String.getBytes(encoding)方法是获取指定编码的byte数组表示,通常gbk/gb2312是2个字节,utf-8是3个字节。如果不指定encoding则取系统默认的encoding。

    [java] view plain copy
     
    1. public static void main(String[] args) throws UnsupportedEncodingException {  
    2.         byte[] b;  
    3.         String str = "测";  
    4.   
    5.         //UTF8编码格式下,一个汉字占3字节  
    6.         b = str.getBytes("UTF-8");  
    7.         System.out.println("UTF8编码格式下,一个汉字占" + b.length + "字节");  
    8.   
    9.         //UTF16编码格式下,一个汉字占4字节  
    10.         b = str.getBytes("UTF-16");  
    11.         System.out.println("UTF16编码格式下,一个汉字占" + b.length + "字节");  
    12.   
    13.         //默认编码UTF-8编码格式下,一个汉字占3字节  
    14.         b = str.getBytes();  
    15.         System.out.println("默认编码" + System.getProperty("file.encoding") + "编码格式下,一个汉字占" + b.length + "字节");  
    16.     }  

    注意,file.encoding在ide和直接用命令行输出可能不一致,ide比如eclipse,idea等可以配置这个编码。如下图的Project Encoding。

    由于JDK是国际版的,在编译的时候,如果我们没有用-encoding参数指定我们的JAVA源程序的编码格式,则javac.exe首先获得我们操作系统默认采用的编码格式,也即在编译java程序时,若我们不指定源程序文件的编码格式,JDK首先获得操作系统的file.encoding参数(它保存的就是操作系统默认的编码格式,如WIN2k,它的值为GBK),然后JDK就把我们的java源程序从file.encoding编码格式转化为JAVA内部默认的UNICODE格式放入内存中。然后,javac把转换后的unicode格式的文件进行编译成.class类文件,此时.class文件是UNICODE编码的,它暂放在内存中,紧接着,JDK将此以UNICODE编码的编译后的class文件保存到我们的操作系统中形成我们见到的.class文件。对我们来说,我们最终获得的.class文件是内容以UNICODE编码格式保存的类文件,它内部包含我们源程序中的中文字符串,只不过此时它己经由file.encoding格式转化为UNICODE格式了。如果IDE设置了file.encoding为UTF-8,那么保存这些代码的文件编码就是UTF-8,如果这时候,在编码为GBK的系统上用javac命令编译这个类,并执行,那么结果会输出乱码。如果在IDE里编译执行,就不会输出乱码。

  • 相关阅读:
    <<Rust程序设计语言>>个人版(4: 所有权)
    《逻辑学入门》笔记(45-66)
    《逻辑学入门》笔记(23-44)
    <<Rust程序设计语言>>个人版(3.3: 函数/3.4: 注释/3.5: 控制流)
    <<Rust程序设计语言>>个人版(1: 入门/2: 编写猜谜游戏)
    网站如何保护用户的密码
    在 Gin 项目中使用 WebSocket
    《逻辑学入门》笔记(1-22)
    浅谈双重认证逻辑
    某大型网络安全活动中遇到的钓鱼邮件
  • 原文地址:https://www.cnblogs.com/xiaohanlin/p/8107911.html
Copyright © 2011-2022 走看看