怀疑首先起于这里:我用 char存储单个的汉字,可是忽然想到,如何char的范围仅有0-65535,如何表示全部的汉字(汉字博大精深,已远超65535个)html
于是我打开eclipse试验charjava
char ch = (char) 65535; System.out.println(ch);
编译器不报错,打印了空(什么都没有),估计此处无字符,能够理解。数组
接下来,我修改成65536eclipse
char ch = (char) 65536; System.out.println(ch);
竟然编译不报错,我有点晕了,不是最大65535么,怎么大于也行?工具
接下来,我找到一个有汉字的84426编码
char ch = (char) 84426; System.out.println(ch);
这里打印出了“䧊”,why?一头雾水,java官方说明文档也是指定其是2个byte,16位的无符号值,也即0-65535,why此处84426均可以,并且打印出了“䧊”spa
后来我又增长ch的值,想其难道是4个字节,直到2^31-1=2147483647时,不报错,加1时2147483648,编译不经过了,提示以下:.net
char ch = (char) 2147483648;//The literal 2147483648 of type int is out of range System.out.println(ch);
注意里面的提示The literal 2147483648 of type int is out of range,我才注意到后面的值实际上是int的范围,因此编译不报错,同理long类型的更大,见code
char ch = (char) 0xFFFFFFFFFFFFFFFFL;//0x表示16进制,后面的L标明为long类型
因此编译不报错,指的是未超事后面类型的范围,因此不报错。htm
那么既然这么大,char范围0-65535那么小,是否有转换规则,是的,见下面
也就是大于65535的int值赋予char时,其会自动mod 65536(此处只针对大于,小于的话就不对了,见下),得出65535范围内的一个unicode
int i = 18890 + 65536; //大于65535即mod,此处加了好几个65535,其仍是表明18890 //int i = 18890 + 65536 + 65536 + 65536; //int i = 18890 - 65536 - 65536; char c = (char)i; System.out.println(c); //mod针对大于65535的还好使,可是其表明的是这个意思 System.out.println(i % 65536); //Integer.valueOf()能获得其表明的unicode值的大小,十进制int表示 System.out.println(Integer.valueOf(c)); char d = '䧊'; int n = (int)d; System.out.println(n);
由上可见,其实“䧊”字的unicode码是18890,而非84426,而且其范围超过0-65535的范围,若是强制转换(char c = (char)i;)java会根据规则自动转换到0-65535区间中的一个。
到这里估计也就明白了,是本身写错了,下面这种方式确定会提示编译错误的
char ch = 65536;// Type mismatch: cannot convert from int to char
也即char的范围的确是0-65535。其实char单引号的写法(
char ch = '䧊';
),只是为了方便记忆和书写,它仍是会转为数字码的形式,也即unicode码存储。
那到了这里
疑问还在,那就是65535的范围如何存储那么多的汉字?
答案是其存不了那么多,最多只能是65535,由于在此范围以外的都会转换到此范围里
并且我试了找到65535以外的汉字(用此工具查看UniToy)来试验,但是始终没法打出这些汉字,遂放弃了
大于65535的汉字,截取一部分
还有点疑问:看下面
public class Test { public static void main(String[] args) { String str= "中"; char x ='中'; byte[] bytes=null; byte[] bytes1=null; byte[] bytes2=null; try { bytes = str.getBytes("gbk"); bytes1 = str.getBytes("utf-8"); bytes2 = charToByte(x); } catch (UnsupportedEncodingException e) { e.printStackTrace(); } System.out.println("bytes 大小:"+bytes.length); System.out.println("bytes1大小:"+bytes1.length); System.out.println("char转换为byte数组大小:"+bytes2.length); System.out.println("byte数组的两个值,其实unicode值的两个字节拆分到数组里了:"+bytes2[0]+"-"+bytes2[1]); } // 如“中”的unicode值为Integer.valueOf(x)=20013 // 二进制为0100 1110 0010 1101 // 拆分出来即为b[0]=0100 1110 b[1]=0010 1101 public static byte[] charToByte(char c) { byte[] b = new byte[2]; b[0] = (byte) ((c & 0xFF00) >> 8); b[1] = (byte) (c & 0xFF); return b; } }
输出:
gbk byte数组大小:2
utf8 byte数组大小:3
char转换为byte数组大小:2
byte数组的两个值,其实unicode值的两个字节拆分到数组里了:78-45
java是用unicode来表示字符,"中"这个中文字符的unicode就是2个字节。
String.getBytes(encoding)方法仅是获取指定编码的byte数组表示,而非java char存储时会占3个字节,只是utf8编码时该占几个字节
一般gbk/gb2312是2个字节,utf-8是3个字节。
若是不指定encoding则取系统默认的encoding。
-----------------------------------------------------------------------------------------------------------
另UTF-8,GBK等这里没涉及,他们只是编解码规则,按必定规则编解码,再和其对应的字符图案对应起来。
其实简单来讲UTF-8就是unicode码如何转换为字符来显示的,其有必定的转换规则,具体见http://my.oschina.net/u/914655/blog/318738处第11项。
另目前汉字范围及与unicode如何对应
GB2312有6763个汉字,GBK有21003个汉字,GB18030-2000有27533个汉字,GB18030-2005有70244个汉字。
Unicode 5.0中,若是不算兼容区,目前有70217个汉字。让咱们比较一下Unicode的70217汉字和GB18030-2005中的70244汉字:
GB18030-2005 | Unicode 5.0 | 对应的Unicode编码 |
CJK统一汉字的20902汉字 | CJK统一汉字的20902汉字 | 0x4E00-0x9FA5 |
CJK统一汉字扩充A的6582汉字 | CJK统一汉字扩充A的6582汉字 | 0x3400-0x4DB5 |
CJK统一汉字扩充B的42711汉字 | CJK统一汉字扩充B的42711汉字 | 0x20000-0x2A6D6 |
CJK部首补充区的14个部首 | 未计入 | 2E81, 2E84, 2E88, 2E8B, 2E8C, 2E97, 2EA7, 2EAA, 2EAE, 2EB3, 2EB6, 2EB7, 2EBB, 2ECA |
CJK兼容汉字区的21个汉字 | 未计入 | F92C, F979, F995, F9E7, F9F1, FA0C, FA0D, FA0E, FA0F, FA11, FA13, FA14, FA18, FA1F, FA20, FA21, FA23, FA24, FA27, FA28, FA29 |
![]() |
CJK统一汉字区新增了这8个字符 | 0x9FB4-0x9FBB |
未计入 | CJK统一汉字区新增的14个字符 | 0x9FA6-0x9FB3 |