实在没什么自信觉得自己能把这个Character能写好,让我和大家都能对她有个透彻的认识,因为我觉得更需要一个专题来说明她,但我还是决定尝试写一点东西。
因为API带来的困惑,所以在此之前,请先阅读几个术语:
Unicode:Unicode(统一码、万国码、单一码)是一种在计算机上使用的字符编码。它为每种语言中的每个字符设定了统一并且唯一的二进制编码
UnicodeData:指定了各种属性,其中包括每个已定义 Unicode 代码点或字符范围的名称和常规类别
BasicMultilingual Plane(BMP) : 从 U+0000 到 U+FFFF 的字符集
代 码 点: char 的int值
高代理项:(\uD800-\uDBFF)
低代理项:(\uDC00-\uDFFF)
增补字符: 代码点大于 U+FFFF 的字符称为增补字符
ASCII码:可以用unicode表示,但unicode会占两个字节
UTF-8:UTF-8是UNICODE的一种变长字符编码又称万国码(用1到6个字节编码UNICODE字符)
不用着急完全明白上面的表述,在我后面的阐述中会把这些连贯起来,自然就明白了。
API注解中有句话: Java 2 平台在 char 数组以及 String 和 StringBuffer 类中使用 UTF-16 表示形式。在这种表现形式中,增补字符表示为一对 char 值,第一个值取自高代理项 范围,即 (\uD800-\uDBFF),第二个值取自低代理项 范围,即 (\uDC00-\uDFFF)。
那就让我们从这句话开始。首先,读懂这句话,表达的意思:
1. char是占2个字节,而char数组以及其衍生物(String等)用到一对char(4个字节);
2. 高代理项指的是一对char的高位,低代理项即第二个char,他们叫做增补字符。
3. Unicode 代码点 用于作为 UTF-16 编码的代码单元的 16 位 char 值,char恒占2个字节
对于增补字符我再说下:
1.(源自:维基百科)Unicode在范围D800-DFFF中不存在任何字符,基本多文种平面中约定了这个范围用于UTF-16扩展标识辅助平面(两个UTF-16表示一个辅助平面字符)。当然,任何编码都是可以被转换到这个范围,但在unicode中他们并不代表任何合法的值。
2.二进制中增补字符总是以“110”开头,以至于在jdk中可能作为一种判定增补字符的方式。
--你可以从这里开始阅读
先说下基础:
char的包装类:Character
char的表现方式:
1. char c1 = ‘c’
2. char c2 = 0(-0),c3 = 65535,c4 = Short.MAX_VALUE*2+1;
3. char c5 = ‘\uFFFF’
(注:c5的表现方式,以‘\u’开头是16进制的表现方式,能且只能有4个字符,而大于c5的值为增补字符。)
char占位:2个字节(16位)
简单介绍几个方法,不过看明白我之前的东西,会更容易理解这些方法。
method:charCount(int codePoint) |static|int
确定表示指定字符(Unicode 代码点)所需的 char 值的数量。如果指定字符等于或大于 0x10000,则该方法返回的值为 2。
method:digit(int codePoint, int radix) |staic|int
返回使用指定基数的字符 ch 的数值。稍微描述的说:
1. isDigit(codePoint) 为true,并且codePoint的Unicode十进制数<指定的基数(radix)
2. 字母范围的话,例如'A':ch - 'A' + 10
3. 2<=radix<=36
我先给个例子:
int z=46; while(z++<120) { print(z); println(Character.digit(z, 16)); } //以后默认声明z //print = System.out.print
out: 发现打印了0-9,a-f,A-F的数值 字符的数值。
OK,那你尝试着把16进制改成36进制试试.
那我们来更通俗的解释这个方法就是: codePoint代表的是字符unicode码int值,通过raidx转换,将返回此codePoint在该进制下代表的数值。
举例:
char c = '0'; int ci = c ;//ci=48 int ca = ’a’; // ca = 97> println(Character.digit(48,11));// 0 println(Character.digit(97,11));// 10
由此推理:在36进制情况下,a-z是不是都代表数字呢?还记得 基本类型-int 篇中提到Integer.parseInt("Kona", 27)吗?这里可以作为思考,后面讲forDigit会揭晓。还有个digit(char,int),这个方法直接传入char值,不支持增补码。
举例:
char c = '\u0061';//a的10进制为97,在16进制a代表10 //c = 'a'; //也能得到 println(Character.digit(c,16));// 10
method:forDigit(int digit, int radix) |static|int
确定使用指定基数的特定数字的字符表示形式。
这个方法与digit相反
见例子:
int i = 36; while(z++out : 0-9,a-z 在36进制之下, a-z代表了10-35的值,Integer.parseInt("Kona", 27)的问题也就不是问题了。
method:getNumericValue(char ch) static|int
返回指定的 Unicode 字符表示的 int 值。
z = 0x007A; while(z++<0XFFFF) { int i = Character.getNumericValue(z); if(i!=-1)println(Integer.toHexString(z)+"--";+i); } //改造一下: z = 0x0000; while(z++<0XFFFF) { int i = Character.getNumericValue(z); if(i==10) { char c = (char)z; print(c); println(Integer.toHexString(z)+"--"+i); } }你能看到你所有感兴趣的字符了。
method:isUpperCase(char ch) |static|boolean
确定指定字符是否为大写字母
A B C D E F G H I J K L M N O P Q R S T U V W X Y Z
'\u00C0' '\u00C1' '\u00C2' '\u00C3' '\u00C4' '\u00C5' '\u00C6' '\u00C7' '\u00C8' '\u00C9' '\u00CA' '\u00CB' '\u00CC' '\u00CD' '\u00CE' '\u00CF' '\u00D0' '\u00D1' '\u00D2' '\u00D3' '\u00D4' '\u00D5' '\u00D6' '\u00D8' '\u00D9' '\u00DA' '\u00DB' '\u00DC' '\u00DD' '\u00DE'method:isLowerCase(char ch) |static|boolean
确定指定字符(Unicode 代码点)是否为小写字母。
method:isTitleCase(int ch) |static|boolean
确定指定字符是否为首字母大写字符。
之前很可能质疑,isUpperCase、isLowerCase需要说明吗?目的就是为了这个方法。
看API的描述:
1. 如果通过 Character.getType(ch) 提供的字符的常规类别类型为 TITLECASE_LETTER,则字符为首字母大写字符。
验证起来很简单:
z = 0x0000; while(z++<0XFFFF) { int i = Character.getType(z); if(i==Character.TITLECASE_LETTER) { print(Integer.toHexString(z)); print((char)z); println(Character.isTitleCase(z)); } }2. 一些字符看似成对的 Latin 字母。例如,有一个看起来像“LJ”的大写字母和一个看起来像“lj”的对应小写字母。第三种形式看起来像“Lj”,这是呈现首字母大写的小写单词时使用的适当形式,比如用于书籍的标题。
但是第2个描述带来了极强的困惑。
为了理解这个,搜集了不少资料。发现其实也很简单。
见实例:http://www.fileformat.info/info/unicode/char/1c5/index.htm以此Dz为例。
看说明:
Upper case U+01C4
Lower case U+01C6
Title case U+01C5
如果按照这个unicode说明,那么
Character.isLowerCase('\u01C6')应该是true而且 Integer.toHexString(Character.toTitleCase('\u01C6')) out: 1c5
果然,除了我们认知的大写小写外,其实还有更多表达upper/lower。就像小时候盐就是吃了,学了化学才知道完全不是那么回事。
这也就验证了jdk在进行字符比较时,出现先转换成大写,再转换成小写进行比较时有意义的。
isSpaceChar(char ch)
确定指定字符是否为 Unicode 空白字符。当且仅当根据 Unicode 标准将字符指定为空白字符时,才认为字符是一个空白字符。如果字符的常规类别的类型为以下类型中的任意一种,则该方法返回 true:
- SPACE_SEPARATOR
- LINE_SEPARATOR
- PARAGRAPH_SEPARATOR
isWhitespace(char ch)
确定指定字符依据 Java 标准是否为空白字符。
- 它是一个 Unicode 空白字符(SPACE_SEPARATOR,LINE_SEPARATOR 或 PARAGRAPH_SEPARATOR),但不是一个不间断空格('\u00A0'、'\u2007' 和 '\u202F')。
- '\u0009',水平制表符。
- '\u000A',换行。
- '\u000B',纵向制表符。
- '\u000C',换页。
- '\u000D',回车。
- '\u001C',文件分隔符。
- '\u001D',组分隔符。
- '\u001E',记录分隔符。
- '\u001F',单元分隔符。
z = 0x0000 while(z++<0XFFFF) { int i = Character.getType(z); if(i==Character.SPACE_SEPARATOR) { print("@SPACE: "+Integer.toHexString(z)); println(Character.isWhitespace(z)); //println(Character.isSpaceChar(z)); }else if(i==Character.LINE_SEPARATOR) println("@LINE: "+Integer.toHexString(z)); else if(i==Character.PARAGRAPH_SEPARATOR) println("@PARAGRAPH: "+Integer.toHexString(z));
} println(Character.isWhitespace('\u0009'));
可以验证 '\u00A0'、'\u2007' 和 '\u202F' 是否如API所描述。
注:我习惯在自己打印的说明参数前加"@",后面加":", 例如:@LINE:
method:hashCode() |int
得到的是char的代码点。 唯一。
method:equal() |void
比较的是value.
这里说明一下。
Character cob1 = 'c';
char va = 99;
cob1.equal(va); //out :true
cob1.equal(99); //out : false 因为99是整型
method:compareTo(Character anotherCharacter) | int
根据数字比较两个 Character 对象。返回:
(this.value – anotherCharacter.value)
好比 cob1.compareTo('0') = 99-48 = 51
而cob1.compareTo((char)0) = 99
不知不觉还是写了很多,但其实并没有把编码问题,以及和String的交互讲明白。争取在后面的String等部分能时候讲明白。
注:对于打印会出现?无法显示的问题,我争取在后面补上关于font.properties的说明。
依然做个小结:
1. 我们了解了ascii码,unicode,UTF-8,UTF-16的一些知识。char知识unicode表示的一部分。
2. 在36进制下,a-z代表10-35, 11进制下只有a被代表。
3. unicode代码点是唯一的,可以用作hashcode
4. 以字符a为例。 a的unicode代码点为97 ,但是a在11-36基数上代表10.
5. 尝试着习惯unicode 16进制的表达方式。正如 a 等价 97 等价 ‘\u0061’