今天粗略读了一下Character的代码,总结如下:
1. 定义了一些 UnicodeSubset (UnicodeBlock) , 每一个UnicodeBlock 代表一个代码段(范围),一部分代码段表示一个UnicodeScript, 任何一个CodePoint都属于某一个UnicodeScript,但不一定属于某一个UnicodeBlock(因为有些代码段不属于任何subset or block--block的name 为null ,这种情况下UnicodeScript就是"UNASSIGNED")
2. 不是很理解为什么 public static UnicodeScript of(int codePoint) 用了 Arrays.binarySearch 而 public static UnicodeBlock of(int codePoint) 却是自己写的二分算法。
UnicodeScript的算法:
int index = Arrays.binarySearch(scriptStarts, codePoint); if (index < 0) index = -index - 2; return scripts[index];
UnicodeBlock的算法:
int top, bottom, current; bottom = 0; top = blockStarts.length; current = top/2; // invariant: top > current >= bottom && codePoint >= unicodeBlockStarts[bottom] while (top - bottom > 1) { if (codePoint >= blockStarts[current]) { bottom = current; } else { top = current; } current = (top + bottom) / 2; } return blocks[current];
3. 为 0-127 (BASIC_LATIN , LATIN_1_SUPPLEMENT, LATIN_EXTENDED_A) 的Code Point建了cache pool, 这个范围的Character new出来都是单例。
4. isValidCodePoint 用了一个简易的小tricky 优化了一下:
// Optimized form of: // codePoint >= MIN_CODE_POINT && codePoint <= MAX_CODE_POINT int plane = codePoint >>> 16; return plane < ((MAX_CODE_POINT + 1) >>> 16);
5. public static int charCount(int codePoint) 没有validate codePoint, 如果codePoint为负数时,也返回1就不太对了。
6. isJavaIdentifierStart 和 isJavaIdentifierPart 可以用来判断一个字符可不可以用来作为Java标识符的开头和部分。
7. 字母 A-Z 可以用来表示基数为10 以上的进制中的一位digit。forDigit方法用来获得某个进制中某字母代表的digit,而digit方法可以得到某个进制中某个digit用哪个字母表示。
8. unicode 为字符定义了一个数值,比如‘A’是10 , ‘Ⅸ’ 是9, 可以用 getNumericValue方法得到一个Code Point的该值。
9. isWiteSpace方法用来判断某个Code Point是不是Java认为的空白字符。
10. isMirrored用来判断字符是否具有镜面对称的字符。比如'(' , '['.
11. 提供了一个 非public 的static char[] toUpperCaseCharArray(int codePoint) 是因为unicode 规定在 BMP中的unicode可能出现其大写是双字符的情况。比如 ß 的大写是 SS。这个方法会被String使用,所以只有String的toUpperCase会将ß 变成SS,而Character不行。
12. getName用来得到Code Point的名称,比如Character.getName(' ') 就是 SPACE, 对于没有名称的character就用它的UnicodeBlock名(下划线用空格替换)加上Code Point代码。比如 Character.getName('\uD801') 就是 HIGH SURROGATES D801。如果没有UnicodeBlock,就返回null。
13. 最后写了一点Test Case :
@Test public void testCharacter() { assertFalse(Character.isLetter('9')); assertFalse(Character.isAlphabetic('\t')); assertTrue(Character.isAlphabetic('Ⅸ')); assertFalse(Character.isJavaIdentifierStart('2')); assertFalse(Character.isJavaIdentifierPart(' ')); assertFalse(Character.isUpperCase(Character.toUpperCase(0))); assertEquals(15, Character.getNumericValue('F')); assertEquals(9 , Character.getNumericValue('Ⅸ')); assertEquals(-1 , Character.digit('E' , 14)); assertEquals(14, Character.digit('E' , 16)); //maximal radix is 36 , because we only have 26 alphabet assertEquals( 0 , Character.forDigit(11, 37)); assertEquals('b' , Character.forDigit(11, 35)); assertFalse(Character.isMirrored(',')); assertTrue(Character.isMirrored('(')); assertEquals('\u00df', Character.toUpperCase('\u00df')); assertEquals( "SS" ,"\u00df".toUpperCase()); assertEquals("SPACE", Character.getName(' ')); assertEquals("HIGH SURROGATES D801", Character.getName('\uD801')); assertNull(Character.getName(0x102E0)); }