Unicode,又称万国码、国际码、统一码、单一码。整理、编码了世界上大部分的文字系统。使得电脑可以用更为简单的方式来呈现和处理文字比如说常用的表情符号,Emoji :happy: , 已经被Unicode 标准化了。每个表情、字符都都分别对应了一组数字,称作代码值(code point,码点)。 以”U+” 开头 ,后面跟着一串十六进制的数字。
Java使用了UTF-16 , Character 中使用一个字节表示一个char. 随着Unicode字符越来越多,两个字节是远远不够的。 Java 就用 两个char来表示,实际上在Character中用一个int来存储,占21bit。
就有了如下的定义, 表示了码点的范围。
public static final int MIN_CODE_POINT = 0x000000;
public static final int MAX_CODE_POINT = 0X10FFFF;
比如说:
String s = "\uD83D\uDE05"; //
Character h = '\uD83D';
Character l = '\uDE05';
s.length() //2
在字符串s
中:
h 表示的是引导代码单元
l 表示的是尾随代理单元
可以使用:
Character.isHighSurrogate(h); //true
Character.isLowSurrogate(l); //true
来判断 是引导代码单元还是尾随代码单元,我习惯用高来代表引导代码单元,用低来表示尾随代码单元
public final
class Character implements java.io.Serializable, Comparable<Character> {
实现了 Serializable , Comparable
支持序列化、和对象比较
public static final int MIN_RADIX = 2;
public static final int MAX_RADIX = 36;
public static final char MIN_VALUE = '\u0000';
public static final char MAX_VALUE = '\uFFFF';
public static final Class<Character> TYPE = (Class<Character>) Class.getPrimitiveClass("char");
public static final int MIN_CODE_POINT = 0x000000;
public static final int MAX_CODE_POINT = 0X10FFFF;
//MIN
public static final char MIN_HIGH_SURROGATE = '\uD800';
public static final char MIN_LOW_SURROGATE = '\uDC00';
//MAX
public static final char MAX_HIGH_SURROGATE = '\uDBFF';
public static final char MAX_LOW_SURROGATE = '\uDFFF';
定义了 0 - 127 的缓存
private static class CharacterCache {
private CharacterCache(){}
static final Character cache[] = new Character[127 + 1];
static {
for (int i = 0; i < cache.length; i++)
cache[i] = new Character((char)i);
}
}
在Unicode中,Blocks被定义位一组连续码位的范围。每个Block会被给予唯一的名称。且区段与区段间不会重叠。
可以在这里查看对每个区块的定义
public static class Subset {
private String name;
protected Subset(String name){...}
public final int hashCode(){...}
public final boolean equals(Object obj){...}
public final String toString(){...}
}
/*
*定义了许多UnicodeBlock
*以及block的开始位置
*/
public static final class UnicodeBlock extends Subset{
private static Map<String, UnicodeBlock> map = new HashMap<>(256);
private UnicodeBlock(String idName, String... aliases) {
this(idName);
for (String alias : aliases)
map.put(alias, this);
}
...
}
of (int)
定义: public static UnicodeBlock of(int codePoint)
传入一个codePoint 返回一个block 对象 , 通过二分查找在blocks中查找
forName ( String )
定义: public static final UnicodeBlock forName(String blockName)
通过block的名称查找,返回一个UnicodeBlock对象。
实际上就是通过map查找的,因为内部有一个key value 的map对象。
Unicode脚本,用一个枚举类来表示。应该是用来表示Unicode支持的字符集。提供的方法和UnicodeBlocks一样。
public Character(char value) {
this.value = value;
}
实现思想和Integer一样,都是通过缓存来提高性能。
public static Character valueOf(char c) {
if (c <= 127) { // must cache
return CharacterCache.cache[(int)c];
}
return new Character(c);
}
public String toString() | 将Value转换为字符串并返回 |
---|---|
public static String toString(char c) | 将c转换为字符串,通过调用String.valueOf转换 |
public String toString() {
char buf[] = {value};
return String.valueOf(buf);
}
public static boolean isWhitespace(char ch) | 传入一个char类型字符,调用下面的方法,判断是否是空格, |
---|---|
public static boolean isWhitespace(int codePoint) | 传入一个codepoint调用具体字符属性的isWhitespace方法 |
public static boolean isWhitespace(int codePoint) {
return CharacterData.of(codePoint).isWhitespace(codePoint);
}
关于CharacterData
CharacterData类是一个抽象类,这个抽象类中定义了许多判断字符属性的抽象方法,这些方法的具体实现都在Character0X类中。其实Character类中有许多对应的方法,CharacterData子类实现抽象类的方法来实现字符属性的判断。我们并不关心这个字符由哪个具体类中的方法来判断,如果以后还增加了一些增补字符,那么只需要实现抽象类并且稍加修改of()方法即可。这就是使用策略模式的好处。
static final CharacterData of(int ch) {
if (ch >>> 8 == 0) { // fast-path
return CharacterDataLatin1.instance;
} else {
switch(ch >>> 16) { //plane 00-16
case(0):
return CharacterData00.instance;
case(1):
return CharacterData01.instance;
case(2):
return CharacterData02.instance;
case(14):
return CharacterData0E.instance;
case(15): // Private Use
case(16): // Private Use
return CharacterDataPrivateUse.instance;
default:
return CharacterDataUndefined.instance;
}
}
}
public static boolean isLetterOrDigit(int codePoint) |
---|
public static boolean isLetterOrDigit(char ch) |
public static boolean isLetterOrDigit(int codePoint) {
return ((((1 << Character.UPPERCASE_LETTER) |
(1 << Character.LOWERCASE_LETTER) |
(1 << Character.TITLECASE_LETTER) |
(1 << Character.MODIFIER_LETTER) |
(1 << Character.OTHER_LETTER) |
(1 << Character.DECIMAL_DIGIT_NUMBER)) >> getType(codePoint)) & 1)
!= 0;
}
就是通过字符或数字的范围来判断。
public static boolean isLetter(int codePoint) |
---|
public static boolean isLetter(char ch) |
public static boolean isLetter(int codePoint) {
return ((((1 << Character.UPPERCASE_LETTER) |
(1 << Character.LOWERCASE_LETTER) |
(1 << Character.TITLECASE_LETTER) |
(1 << Character.MODIFIER_LETTER) |
(1 << Character.OTHER_LETTER)) >> getType(codePoint)) & 1)
!= 0;
}
同上,也是通过范围判断
public static boolean isDigit(int codePoint) |
---|
public static boolean isDigit(char ch) |
public static boolean isDigit(int codePoint) {
return getType(codePoint) == Character.DECIMAL_DIGIT_NUMBER;
}
也可以说是CharacterData中定义的字符
public static boolean isDefined(int codePoint) |
---|
public static boolean isDefined(char ch) |
public static boolean isDefined(int codePoint) {
return getType(codePoint) != Character.UNASSIGNED;
}
是大写还是小写
public static boolean isLowerCase(int codePoint) |
---|
public static boolean isUpperCase(int codePoint) |
public static boolean isUpperCase(int codePoint) {
return getType(codePoint) == Character.UPPERCASE_LETTER ||
CharacterData.of(codePoint).isOtherUppercase(codePoint);
}
public static boolean isLowerCase(int codePoint) {
return getType(codePoint) == Character.LOWERCASE_LETTER ||
CharacterData.of(codePoint).isOtherLowercase(codePoint);
}
public static boolean isJavaIdentifierStart(int codePoint) |
---|
public static boolean isJavaIdentifierStart(char ch) |
确定指定字符是否允许作为Java标识符中的第一个字符。
当且仅当以下之一为真时,字符才能启动Java标识符:
isLetter(ch)
返回true
getType(ch)
返回LETTER_NUMBER
ch
是货币符号(如'$'
)ch
是连接标点符号(如'_'
)·· public static boolean isJavaIdentifierStart(int codePoint) {
return CharacterData.of(codePoint).isJavaIdentifierStart(codePoint);
}
public static int digit(int codePoint, int radix) | 将codepoint代表的字符转为int类型,基于radix |
---|---|
public static int digit(char ch, int radix) | 调用digit(int,int) |
public static int digit(int codePoint, int radix) {
return CharacterData.of(codePoint).digit(codePoint, radix);
}
也是具体字符调用具体方法处理。不用关心具体怎么实现的。其实也就是判断范围或具体的值。
test
@Test
public void digitTest(){
char a = 'A';
System.out.println(Integer.valueOf(a));
System.out.println(Character.digit('A', 16));
}
结果:
65
10
public static char toLowerCase(char ch) | 调用toLowerCase(int),转小写 |
---|---|
public static int toLowerCase(int codePoint) | 转小写,具体调用 |
public static char toUpperCase(char ch) | 调用toUpperCase(int),转大写 |
public static int toUpperCase(int codePoint) | 转大写,具体调用 |
public static int toLowerCase(int codePoint) {
return CharacterData.of(codePoint).toLowerCase(codePoint);
}
public static int toUpperCase(int codePoint) {
return CharacterData.of(codePoint).toUpperCase(codePoint);
}
public static char[] toChars(int codePoint) |
---|
public static int toChars(int codePoint, char[] dst, int dstIndex) |
public static char[] toChars(int codePoint) {
if (isBmpCodePoint(codePoint)) {
return new char[] { (char) codePoint };
} else if (isValidCodePoint(codePoint)) {
char[] result = new char[2];
toSurrogates(codePoint, result, 0);
return result;
} else {
throw new IllegalArgumentException();
}
}
如果码点是bmp 就直接转换成char数组,毕竟没有高低位之分。
如果是补充代码点,则将codepoint拆成字符数组。
public static char forDigit(int digit, int radix) {
if ((digit >= radix) || (digit < 0)) {
return '\0';
}
if ((radix < Character.MIN_RADIX) || (radix > Character.MAX_RADIX)) {
return '\0';
}
if (digit < 10) {
return (char)('0' + digit);
}
return (char)('a' - 10 + digit);
}
如果数字比基数大或者数字不合法,返回‘\0’
如果超过基数上下限,返回‘\0’
然后再将数字转数字的字符形式
返回字符对应的UnicodeBlock名称,如果是未分配的返回null
public static String getName(int codePoint) {
if (!isValidCodePoint(codePoint)) {
throw new IllegalArgumentException();
}
String name = CharacterName.get(codePoint);
if (name != null)
return name;
if (getType(codePoint) == UNASSIGNED)
return null;
UnicodeBlock block = UnicodeBlock.of(codePoint);
if (block != null)
return block.toString().replace('_', ' ') + " "
+ Integer.toHexString(codePoint).toUpperCase(Locale.ENGLISH);
// should never come here
return Integer.toHexString(codePoint).toUpperCase(Locale.ENGLISH);
}
返回字符或码点对应的类型,这个类型用int来表示。在Character中有定义(这里都用的是byte类型)。 比如说:
public static final byte UNASSIGNED = 0;
public static final byte UPPERCASE_LETTER = 1;
这个方法挺重要的,是用来区分Unicode中不同类型的字符的。 有很多方法都使用了这个方法,比如说判断数字、字母等方法。
public static int getType(int codePoint) {
return CharacterData.of(codePoint).getType(codePoint);
}
public static boolean isValidCodePoint(int codePoint) | 判断是否是有效的codepoint |
---|---|
public static boolean isBmpCodePoint | 是否是bmp codepoint |
public static boolean isSupplementaryCodePoint | 是否是补充字符 |
public static boolean isHighSurrogate(char ch) | 是否是引导代码单元 |
public static boolean isLowSurrogate(char ch) | 是否是后置代码单元 |
public static boolean isSurrogate(char ch) | 是否是代码单元 / 补充字符 |
public static boolean isSurrogatePair(char high, char low) | 确定指定的一对是否分别是引导代码单元和后置代码单元 |
是否是有效的codepoint
public static boolean isValidCodePoint(int codePoint) {
// Optimized form of:
// codePoint >= MIN_CODE_POINT && codePoint <= MAX_CODE_POINT
int plane = codePoint >>> 16;
return plane < ((MAX_CODE_POINT + 1) >>> 16);
}
后面16位不关心,只需要小于码点的最高位就好。
public static boolean isBmpCodePoint(int codePoint) {
return codePoint >>> 16 == 0;
}
范围不超过2个字节的都是bmp码点
public static int toCodePoint(char high, char low) | 传入高位和低位,来构造一个codepoint,返回值是一个int类型 |
---|---|
public static int codePointAt(CharSequence seq, int index) | 传入一个字符序列和index ,通过index返回字符序列中的一个codepointdd |
public static int codePointAt(char[] a, int index, int limit) | 带limit的 |
static int codePointAtImpl(char[] a, int index, int limit) | codePointAt的具体实现,上面两个都是调用这个方法的 |
public int compareTo(Character anotherCharacter) {
return compare(this.value, anotherCharacter.value);
}
public static int compare(char x, char y) {
return x - y;
}
逻辑很简单,就是 x-y
public static char reverseBytes(char ch) {
return (char) (((ch & 0xFF00) >> 8) | (ch << 8));
}
按字节进行逆序。高低位互换一下。
字符序列的定义:
public interface CharSequence{
int length();
char charAt(int index);
CharSequence subSequence(int start, int end);
public String toString();
public default IntStream chars() {...}
public default IntStream codePoints() {...}
}
是一个接口,它的具体实现类有常见的String,StringBuffer等。它是一个描述字符串结构的接口,此接口对多种不同的char访问的统一接口。
https://www.cnblogs.com/DDgougou/articles/13895931.html
https://juejin.cn/post/6844903991378182158#heading-28
基于 JDK1.8 的环境