Java 字符集 编码

Java默认的字符集是Unicode(占两个字节byte,一个字节=8比特位bit)

详解:

字符集 编码

Unicode 是「字符集」;UTF-8是「编码规则」(是使用最广的一种Unicode的实现方式)

字符集:为每一个字符分配一个唯一的ID(码位)

编码规则:将码位转换为字节序列的规则(用什么方式存储)

英文/字节

中文/字节

Utf-8(变长)

1

3

Utf-16

2

3-4

GBK

1

2

ISO8859-1

1

1

Unicode

2

2(标点也是)

ASCII

1

2

Java的处理方法:

编码问题存在两个方面:JVM之内和JVM之外。

1.编译器把Java文件编译后形成class

这里Java文件的编码可能有多种多样(可以为utf-8(常用)),但Java编译器会自动将这些编码按照Java文件的编码格式正确读取后产生class文件,这里的class文件编码是Unicode编码(具体说是UTF-16编码)。也就是说完成了从UTF-8编码的文件转成与平台无关的.class文件了,把UTF-8编码方式转成了Unicode。一旦编译成.class文件,就不用在乎关于我们程序源码的什么UTF-8编码了

因此,在Java代码中定义一个字符串String s="汉字";

不管在编译前java文件使用何种编码,在编译后成class后,他们都是一样的----Unicode编码表示。

2.JVM中的编码

在JVM内部,统一使用Unicode表示,当着字符从JVM内部移动到外部时(即保存为文件系统中的一个文件内容时),就进行了编码转换,使用了具体的编码方案。因此也可以说,所有的编码转换只发生在边界的地方,也就是各种输入/输出流的起作用的地方。

JVM加载class文件读取时候使用Unicode编码方式正确读取class文件,那么原来定义的String s="汉字";在内存中的表现形式是Unicode编码。

问题

在java中,一个字符等于多少字节?

或者更详细的问:在java中,一个英文字符等于多少字节?一个中文字符等于多少字节?

Java采用unicode来表示字符,java中的一个char是2个字节,一个中文或英文字符的unicode编码都占2个字节,但如果采用其他编码方式,一个字符占用的字节数则各不相同。

代码验证如下:

public static voidmain(String[] args) {
String str= "测";char x = '测';byte[] byteStr =str.getBytes();byte[] byteChar =charToByte(x);
System.out.println("byteStr :" + byteStr.length); //byteStr :3
System.out.println("byteChar:" + byteChar.length); //byteChar:2
}//通过移位获取char类型的byte数组
public static byte[] charToByte(charc) {byte[] b = new byte[2];
b[0] = (byte) ((c & 0xFF00) >> 8);
b[1] = (byte) (c & 0xFF);returnb;
}

获取系统编码

System.out.println("系统默认编码:" + System.getProperty("file.encoding")); //查询结果UTF-8

System.out.println("系统默认字符编码:" + Charset.defaultCharset()); //查询结果UTF-8

System.out.println("系统默认语言:" + System.getProperty("user.language")); //查询结果zh

getBytes()方法详解

另外解释一下上边使用的getBytes()方法

在Java中,String的getBytes()方法是得到一个操作系统默认的编码格式的字节数组。这表示在不同的操作系统下,返回的东西不一样!

1.str.getBytes();  如果括号中不写charset,则采用的是Sytem.getProperty("file.encoding"),即当前文件的编码方式,

2.str.getBytes("charset");//指定charset,即将底层存储的Unicode码解析为charset编码格式的字节数组方式

3.String  str=new String(str.getBytes("utf-8"),"gbk")); //将已经解析出来的字节数据转化为gbk编码格式的字符串,在内存中即为gbk格式的字节数组转为Unicode去交互传递

引申

问:

"a".getBytes("Unicode").length //结果为 4

上边已经说过Unicode一个字符占两个字节,这里为什么是4字节不是2字节?

为什么Unicode4个字节

使用for循环遍历得到的byte数组(还是使用字符 a ):

-2 -1 0 97

发现前边多了一个-2-1 ,这其实是一个字节的BOM标志。

UNICODE 是一种字符集,在Java中直接使用Unicode转码时会按照UTF-16LE的方式拆分,由于UTF-16分为UTF-16LE和UTF-16BE,也就是小端序和大端序,因此在网络传过程中,无法判断是LE还是BE 序的,因此需要加上一个额外的字节序 BOM 头。BOM头的字符是一个特殊的字符,其Unicode编码为U+FEFF,字符名为“ZERO WIDTH NON-BREAKING SPACE”,根据RFC2781 3.2节规定,开头两个字节为FE FF的称为Big-Endian,开头为FF FE的称为Little-Endian。

关于utf-16的解释:utf-16的方式包含2种字节序,Big Endian字节序和Little Endian字节序:

UTF-16 Big Endian:FEFF (没有含义在UCS-2中),其中FEFF为标示码

UTF-16 Little Endian:FFFE (没有含义在UCS-2中),java默认选择Little Endian字节序

因此,你直接使用 Unicode 转换字节的话,也就是按UTF-16LE方式进行解码,会额外地加上BOM的两个字节FF FE。

解决办法:

可以使用UnicodeBigUnmarked编码

"a".getBytes("UnicodeBigUnmarked").length //结果为2

参考:

http://bbs.itheima.com/thread-101106-1-1.html