目录
一、实现机制
Character类用于对字符类型的数据进行包装和处理,具体实现主要依赖于CharacterData类及CharacterDataLatin1、CharacterData00、CharacterData01、CharacterData02等子类。
其中CharacterDataLatin1类用于对ISO-8859-1字符集(即Latin1。0~127:基本ASCII字符集,128~255:扩展ASCII字符集)中的字符进行处理,CharacterData00等主要用于对Unicode字符集中的字符进行处理。
处理方法如下(以CharacterDataLatin1为例):
1、首先对字符属性进行编码,并用32bit的空间存储:
31 | 30 | 29 | 28 | 27 | 26 | 25 | 24 | 23 | 22 | 21 | 20 | 19 | 18 | 17 | 16 |
15 | 14 | 13 | 12 | 11 | 10 | 09 | 08 | 07 | 06 | 05 | 04 | 03 | 02 | 01 | 00 |
说明:
Bit 31: 1位,存镜像属性。镜像属性:指有开始有结束的、成对出现的特性。如: ()、{}、[]等。
Bit 27~30: 4位,方向性特性。
Bit 18~26: 9位,存转换大小写的有符号偏移量。大小写互换时用。
Bit 17: 1位,若值为1,则加上有符号偏移量将字符转换为小写。此时字符为大写。
Bit 16: 1位,如果为1,减去有符号偏移量将字符转换为大写。此时字符为小写。
Bit 15: 1位,如果为1,则此字符有一个与其大写字符等价的字符(可能是其本身)
Bit 12~14: 3位,指明字符是否可以作为标识符的一部分。
=0:不能是标识符的一部分
=1:可忽视的控制;可以用作Unicode标识符或Java标识符首字符意外的部分
=2:可以用作Java标识符首字符以外的部分,但不能作用Unicode标识符首字符以外的部分
=3:可以用作Unicode标识符或Java标识符首字符以外的部分
=4:是Java空白字符
=5:可以用作Java标识符(首字符和后续部分),可以用作Unicode标识符的后续部分,但不能用作首字符(下划线)
=6:可以Java标识符(首字符和后续部分),但不能用作Unicode标识符(首字符和后续部分)($)
=7:可以用作Unicode标识符或Java标识符
从上述可以看出:若12~14位等于5、6、7时可以用作Java标识符的首字符,等于1、2、3、5、6、7时可以用作Java标识符除首字符外的后续部分。
Bit 10~11:
=0:此字符没有数字属性
=1:数字偏移量 + 字符编码,然后用0x1F掩蔽将产生所需的数值
=2:此字符有一个“奇怪”的数值
=3:Java超十进制数字(a~z):数字偏移量 + 字符编码,然后使用0x1F掩蔽,然后添加10将生成所需的数值
Bit 05~09: 数字偏移量:
用于数字字符与其数值之间的转换。数字字符有:0~9a~z(大小写一样),与之对应的十进制数值是:0~35。
数字字符0~9的偏移量:0x10, 大小写字母A~Za~z的偏移量是:0x1f
Bit 00~04: 字符类型:
=1: 大写字符
=2: 小写字符
=9: 数字
=12: 空格
=20~30: 标点
2、操作:
由上述可知,32bit中存储着字符的不同属性,我们可以通过屏蔽法(位与运算)获取对应位的值来获取相应的属性。
1)通过如下代码可以获取ASCII值为0~127的字符的类型
void printType() { out.println("ASCII值为:1~127的后5位:"); for(int i = 0; i <= 127; i++) { if(i%10 == 0 && i !=0) out.println(""); out.printf("%3d:%x\t", i, (CharacterDataLatin.A[i] & 0x1f)); } } |
这里的重点是"CharacterDataLatin.A[i] & 0x1f",A数组中存的是字符的32位特征码,0x1F转换成二进制是00011111,二者与运算后,即可获得标识字符类型的最后5位。
其他特征的获取方法类似。
2)方法实现:
int getNumericValue(int ch) { switch (val & 0xC00) { //取第10~11位, |
int toLowerCase(int ch) { if (((val & 0x00020000) != 0) && //取第17位,不等于0说明可以转小写 } |
int toUpperCase(int ch) { if ((val & 0x00010000) != 0) { //取第16位,不等于0说明可以转大写 |
以上方法如果理解了,其他方法就很容易懂了
三、设计模式
在Character的实现中,主要用到了如下设计模式:
1、简单工厂模式、享元模式:
private static class CharacterCache { static final Character[] cache; static { // Load and use the archived cache if it exists public static Character valueOf(char c) { |
在以上代码中,只要ASCII值>127时,valueOf就会新构建一个Character对象,在此可以看成是简单工厂;当ASCII值<=127时,valueOf就从CharacterCache.cache缓存中获取,用到了享元模式。
说明:
1)若要构建的产品不多,就可用简单工厂模式来实现。通常是用一个静态方法来集中构建某一产品。
2)若要用到大量的相同或相似的细粒度对象,为节省资源,通常将这些对象放于缓存中,需要时直接从缓存获取,这就是享元模式。享元模式的核心是“共享”实例。
2、单例模式:
static final CharacterDataLatin1 instance = new CharacterDataLatin1(); private CharacterDataLatin1() {}; |
如上代码,将CharacterDataLatin1类的构造方式设置成私有的,外部类只能通过instance属性获得其实例。
通常,在一个应用中,某类只需要一个实例时,就可以用单例模式,如:数据库连接。
设计模式的具体内容,在此不做详述,但要注意的是:享元模式和单例模式都存在线程安全问题,使用时要注意这一点。
以上内容若存在问题,请指正,谢谢!