Java java.lang.Character源码分析

Unicode相关知识

Unicode,又称万国码、国际码、统一码、单一码。整理、编码了世界上大部分的文字系统。使得电脑可以用更为简单的方式来呈现和处理文字比如说常用的表情符号,Emoji :happy: , 已经被Unicode 标准化了。每个表情、字符都都分别对应了一组数字,称作代码值(code point,码点)。 以”U+” 开头 ,后面跟着一串十六进制的数字。

Java 对Unicode的支持

​ 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

支持序列化、和对象比较

属性

Radix范围

public static final int MIN_RADIX = 2;
public static final int MAX_RADIX = 36;

Value范围

public static final char MIN_VALUE = '\u0000';
public static final char MAX_VALUE = '\uFFFF';

Class类型

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);
        }
    }

UnicodeBlocks

在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对象。

UnicodeScript

Unicode脚本,用一个枚举类来表示。应该是用来表示Unicode支持的字符集。提供的方法和UnicodeBlocks一样。

方法

构造器

public Character(char value) {
    this.value = value;
}

ValueOf

实现思想和Integer一样,都是通过缓存来提高性能。

    public static Character valueOf(char c) {
        if (c <= 127) { // must cache
            return CharacterCache.cache[(int)c];
        }
        return new Character(c);
    }

toString

public String toString() 将Value转换为字符串并返回
public static String toString(char c) 将c转换为字符串,通过调用String.valueOf转换
public String toString() {
	char buf[] = {value};
	return String.valueOf(buf);
}

对char的一些判断

空格

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;
}

合法的(Unicode已定义的)

也可以说是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);
}

允许作为Java标识符中的第一个字符

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);
}

toChars 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拆成字符数组。

forDigit 数字转字符

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’

然后再将数字转数字的字符形式

get???

getName

返回字符对应的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);
}

getType

返回字符或码点对应的类型,这个类型用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);
}

有关CodePoint的处理

对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) 确定指定的一对是否分别是引导代码单元和后置代码单元

isValidCodePoint

是否是有效的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位不关心,只需要小于码点的最高位就好。

isBmpCodePoint

public static boolean isBmpCodePoint(int codePoint) {
    return codePoint >>> 16 == 0;
}

范围不超过2个字节的都是bmp码点

对codepoint处理

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的具体实现,上面两个都是调用这个方法的

comparaTo 比较

public int compareTo(Character anotherCharacter) {
    return compare(this.value, anotherCharacter.value);
}
public static int compare(char x, char y) {
    return x - y;
}

逻辑很简单,就是 x-y

reverseBytes 字节逆序

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 的环境

你可能感兴趣的:(源码分析,java,character)