《Java 编程的逻辑》笔记——第7章 常用基础类(一)

声明:

本博客是本人在学习《Java 编程的逻辑》后整理的笔记,旨在方便复习和回顾,并非用作商业用途。

本博客已标明出处,如有侵权请告知,马上删除。

开头语

本章介绍 Java 编程中一些常用的基础类,探讨它们的用法、应用和实现原理,这些类有:

  • 各种包装类
  • 文本处理的类 String 和 StringBuilder
  • 数组操作的类 Arrays
  • 日期和时间处理
  • 随机

7.1 包装类

Java 有八种基本类型,每种基本类型都有一个对应的包装类。

包装类是什么呢?它是一个类,内部有一个实例变量,保存对应的基本类型的值,这个类一般还有一些静态方法、静态变量和实例方法,以方便对数据进行操作。

Java 中,基本类型和对应的包装类如表 7-1 所示:

在这里插入图片描述

包装类也都很好记,除了 Integer 和 Character 外,其他类名称与基本类型基本一样,只是首字母大写。

包装类有什么用呢?Java 中很多代码(比如后续文章介绍的集合类)只能操作对象,为了能操作基本类型,需要使用其对应的包装类,另外,包装类提供了很多有用的方法,可以方便对数据的操作。

下面先介绍各个包装类的基本用法及其共同点,然后重点介绍 Integer 和 Character。

7.1.1 基本用法

我们先来看各个基本类型和其包装类是如何转换的,我们直接看代码:

Boolean

boolean b1 = false;
Boolean bObj = Boolean.valueOf(b1);
boolean b2 = bObj.booleanValue();

Byte

byte b1 = 123;
Byte byteObj = Byte.valueOf(b1);
byte b2 = byteObj.byteValue();

Short

short s1 = 12345;
Short sObj = Short.valueOf(s1);
short s2 = sObj.shortValue();

Integer

int i1 = 12345;
Integer iObj = Integer.valueOf(i1);
int i2 = iObj.intValue();

Long

long l1 = 12345;
Long lObj = Long.valueOf(l1);
long l2 = lObj.longValue();

Float

float f1 = 123.45f;
Float fObj = Float.valueOf(f1);
float f2 = fObj.floatValue();

Double

double d1 = 123.45;
Double dObj = Double.valueOf(d1);
double d2 = dObj.doubleValue(); 

Character

char c1 = 'A';
Character cObj = Character.valueOf(c1);
char c2 = cObj.charValue(); 

这些代码结构是类似的,每种包装类都有一个静态方法 valueOf(),接受基本类型,返回引用类型,也都有一个实例方法 xxxValue() 返回对应的基本类型。

将基本类型转换为包装类的过程,一般称为"装箱",而将包装类型转换为基本类型的过程,则称为"拆箱"。装箱/拆箱写起来比较啰嗦,Java 1.5 以后引入了自动装箱和拆箱技术,可以直接将基本类型赋值给引用类型,反之亦可,如下代码所示:

Integer a = 100;
int b = a;

自动装箱/拆箱是 Java 编译器提供的能力,背后,它会替换为调用对应的 valueOf()/xxxValue(),比如说,上面的代码会被 Java 编译器替换为:

Integer a = Integer.valueOf(100);
int b = a.intValue();

每种包装类也都有构造方法,可以通过 new 创建,比如说:

Integer a = new Integer(100);
Boolean b = new Boolean(true);
Double d = new Double(12.345);
Character c = new Character('马');

那到底应该用静态的 valueOf 方法,还是使用 new 呢?一般建议使用 valueOf。new 每次都会创建一个新对象,而除了 Float 和 Double 外的其他包装类,都会缓存包装类对象,减少需要创建对象的次数,节省空间,提升性能,后续我们会分析其具体代码。

7.1.2 共同点

各个包装类有很多共同点,比如,都重写了 Object 中的一些方法,都实现了 Comparable 接口,都有一些与 String 有关的方法,大部分都定义了一些静态常量,都是不可变的。下面具体介绍。

7.1.2.1 重写 Object 方法

所有包装类都重写了 Object 类的如下方法:

boolean equals(Object obj)
int hashCode()
String toString()

我们逐个来看下。

equals

equals 用于判断当前对象和参数传入的对象是否相同,Object 类的默认实现是比较地址,对于两个变量,只有这两个变量指向同一个对象时,equals 才返回 true,它和比较运算符(==)的结果是一样的

但,equals 应该反映的是对象间的逻辑相等关系,所以这个默认实现一般是不合适的,子类需要重写该实现。所有包装类都重写了该实现,实际比较用的是其包装的基本类型值,比如说,对于 Long 类,其 equals 方法代码是:

public boolean equals(Object obj) {
    
    
    if (obj instanceof Long) {
    
    
        return value == ((Long)obj).longValue();
    }
    return false;
}

对于 Float,其实现代码为:

public boolean equals(Object obj) {
    
    
    return (obj instanceof Float)
           && (floatToIntBits(((Float)obj).value) == floatToIntBits(value));
}

Float 有一个静态方法 floatToIntBits(),将 float 的二进制表示看做 int。需要注意的是,只有两个 float 的二进制表示完全一样的时候,equals 才会返回 true。在 2.2 节的时候,我们提到小数计算是不精确的,数学概念上运算结果一样,但计算机运算结果可能不同,比如说,看下面代码:

Float f1 = 0.01f;
Float f2 = 0.1f*0.1f;
System.out.println(f1.equals(f2));
System.out.println(Float.floatToIntBits(f1));
System.out.println(Float.floatToIntBits(f2)); 

输出为:

false
1008981770
1008981771

也就是,两个浮点数不一样,将二进制看做整数也不一样,相差为 1。

Double 的 equals 方法与 Float 类似,它有一个静态方法 doubleToLongBits,将 double 的二进制表示看做 long,然后再按 long 比较。

hashCode

hashCode 返回一个对象的哈希值,哈希值是一个 int 类型的数,由对象中一般不变的属性映射得来,用于快速对对象进行区分、分组等。一个对象的哈希值不能变,相同对象的哈希值必须一样。不同对象的哈希值一般应不同,但这不是必须的,可以有不同对象但哈希值相同的情况

比如说,对于一个班的学生对象,hashCode 可以是学生的出生月日,出生日期是不变的,不同学生生日一般不同,分布比较均匀,个别生日相同的也没关系。

hashCode 和 equals 方法联系密切,对两个对象,如果 equals 方法返回 true,则 hashCode 也必须一样。反之不要求,equal 返回 false 时,hashCode 可以一样,也可以不一样,但应该尽量不一样。hashCode 的默认实现一般是将对象的内存地址转换为整数,子类重写 equals 时,也必须重写 hashCode。之所以有这个规定,是因为 Java API 中很多类依赖于这个行为,尤其是集合中的一些类。

包装类都重写了 hashCode,根据包装的基本类型值计算 hashCode,对于 Byte, Short, Integer, Character,hashCode 就是其内部值,代码为:

public int hashCode() {
    
    
    return (int)value;
}

对于 Boolean,hashCode 代码为:

public int hashCode() {
    
    
    return value ? 1231 : 1237;
}

根据基类类型值返回了两个不同的数,为什么选这两个值呢?它们是质数,即只能被 1 和自己整除的数,后续我们会讲到,质数比较好,但质数很多,为什么选这两个呢,这个就不得而知了,大概是因为程序员对它们有特殊的偏好吧。

对于 Long,hashCode 代码为:

public int hashCode() {
    
    
    return (int)(value ^ (value >>> 32));
}

是高 32 位与低 32 位进行位异或操作。

对于 Float,hashCode 代码为:

public int hashCode() {
    
    
    return floatToIntBits(value);
}

与 equals 方法类似,将 float 的二进制表示看做了 int。

对于 Double,hashCode 代码为:

public int hashCode() {
    
    
    long bits = doubleToLongBits(value);
    return (int)(bits ^ (bits >>> 32));
}

与 equals 类似,将 double 的二进制表示看做 long,然后再按 long 计算 hashCode。

关于 equals 和 hashCode,我们还会在后续的章节中碰到,并进行进一步说明。

toString

每个包装类也都重写了 toString 方法,返回对象的字符串表示,这个一般比较自然,我们就不赘述了。

7.1.2.2 Comparable

每个包装类也都实现了 Java API 中的 Comparable 接口,Comparable 接口代码如下:

public interface Comparable<T> {
    
    
    public int compareTo(T o);
}

<T> 是泛型语法,我们后续文章介绍,T 表示比较的类型,由实现接口的类传入。接口只有一个方法 compareTo,当前对象与参数对象进行比较,在小于、等于、大于参数时,应分别返回 -1,0,1

各个包装类的实现基本都是根据基本类型值进行比较,不再赘述。对于 Boolean,false 小于 true。对于 Float 和 Double,存在和 equals 一样的问题,0.01 和 0.1*0.1 相比的结果并不为 0。

7.1.2.3 包装类和 String

除了 toString 方法外,包装类还有一些其他与 String 相关的方法。

除了 Character 外,每个包装类都有一个静态的 valueOf(String) 方法,根据字符串表示返回包装类对象,如:

Boolean b = Boolean.valueOf("true");
Float f = Float.valueOf("123.45f");

也都有一个静态的 parseXXX(String) 方法,根据字符串表示返回基本类型值,如:

boolean b = Boolean.parseBoolean("true");
double d = Double.parseDouble("123.45");

都有一个静态的 toString() 方法,根据基本类型值返回字符串表示,如:

System.out.println(Boolean.toString(true));
System.out.println(Double.toString(123.45));

输出:

true
123.45 

对于整数类型,字符串表示除了默认的十进制外,还可以表示为其他进制,如二进制、八进制和十六进制,包装类有静态方法进行相互转换,比如:

System.out.println(Integer.toBinaryString(12345)); //输出2进制
System.out.println(Integer.toHexString(12345)); //输出16进制
System.out.println(Integer.parseInt("3039", 16)); //按16进制解析

输出为:

11000000111001
3039
12345

7.1.2.4 常用常量

包装类中除了定义静态方法和实例方法外,还定义了一些静态变量。

Boolean 类型:

public static final Boolean TRUE = new Boolean(true);
public static final Boolean FALSE = new Boolean(false);

所有数值类型都定义了 MAX_VALUE 和 MIN_VALUE,表示能表示的最大/最小值,比如,对 Integer:

public static final int   MIN_VALUE = 0x80000000;
public static final int   MAX_VALUE = 0x7fffffff;

Float 和 Double 还定义了一些特殊数值,比如正无穷、负无穷、非数值,如 Double 类:

public static final double POSITIVE_INFINITY = 1.0 / 0.0;
public static final double NEGATIVE_INFINITY = -1.0 / 0.0;
public static final double NaN = 0.0d / 0.0;

7.1.2.5 Number

六种数值类型包装类有一个共同的父类 Number,Number 是一个抽象类,它定义了如下方法:

byte byteValue()
short shortValue()                
int intValue()
long longValue()
float floatValue()
double doubleValue()

通过这些方法,包装类实例可以返回任意的基本数值类型。

7.1.2.6 不可变性

包装类都是不可变类,所谓不可变就是,实例对象一旦创建,就没有办法修改了。这是通过如下方式强制实现的:

  • 所有包装类都声明为了 final,不能被继承
  • 内部基本类型值是私有的,且声明为了 final
  • 没有定义 setter 方法

为什么要定义为不可变类呢?不可变使得程序可以更为简单安全,因为不用操心数据被意外改写的可能了,可以安全的共享数据,尤其是在多线程的环境下。关于线程,我们后续文章介绍。

7.1.3 剖析 Integer 与二进制算法

本节继续探讨包装类,主要介绍 Integer 类,下节介绍 Character 类,Long 与 Integer 类似,就不再单独介绍了,其他类基本已经介绍完了,不再赘述。

一个简单的 Integer 还有什么要介绍的呢?它有一些二进制操作,我们来看一下,另外,我们也分析一下它的 valueOf 实现。

为什么要关心实现代码呢?大部分情况下,确实不用关心,我们会用它就可以了,我们主要是为了学习,尤其是其中的二进制操作,二进制是计算机的基础,但代码往往晦涩难懂,我们希望对其有一个更为清晰深刻的理解。

7.1.3.1 位翻转

Integer 有两个静态方法,可以按位进行翻转:

public static int reverse(int i)
public static int reverseBytes(int i)

位翻转就是将 int 当做二进制,左边的位与右边的位进行互换,reverse 是按位进行互换,reverseBytes 是按 byte 进行互换。我们来看个例子:

int a = 0x12345678;
System.out.println(Integer.toBinaryString(a));

int r = Integer.reverse(a);
System.out.println(Integer.toBinaryString(r));

int rb = Integer.reverseBytes(a);
System.out.println(Integer.toHexString(rb));

a 是整数,用十六进制赋值,首先输出其二进制字符串,接着输出 reverse 后的二进制,最后输出 reverseBytes 的十六进制,输出为:

10010001101000101011001111000
11110011010100010110001001000
78563412

reverseBytes 是按字节翻转,78 是十六进制表示的一个字节,12 也是,所以结果 78563412 是比较容易理解的。

二进制翻转初看是不对的,这是因为输出不是 32 位,输出时忽略了前面的 0,我们补齐 32 位再看:

00010010001101000101011001111000
00011110011010100010110001001000

这次结果就对了。

7.1.3.2 循环移位

Integer 有两个静态方法可以进行循环移位:

public static int rotateLeft(int i, int distance)
public static int rotateRight(int i, int distance) 

rotateLeft 是循环左移,rotateRight 是循环右移,distance 是移动的位数。所谓循环移位,是相对于普通的移位而言的,普通移位,比如左移 2 位,原来的最高两位就没有了,右边会补 0,而如果是循环左移两位,则原来的最高两位会移到最右边,就像一个左右相接的环一样。我们来看个例子:

int a = 0x12345678;
int b = Integer.rotateLeft(a, 8);
System.out.println(Integer.toHexString(b));

int c = Integer.rotateRight(a, 8);
System.out.println(Integer.toHexString(c))

b 是 a 循环左移 8 位的结果,c 是 a 循环右移 8 位的结果,所以输出为:

34567812
78123456

7.1.3.3 valueOf 的实现

前面我们提到,创建包装类对象时,可以使用静态的 valueOf 方法,也可以直接使用 new,但建议使用 valueOf,为什么呢?我们来看 valueOf 的代码:

public static Integer valueOf(int i) {
    
    
    assert IntegerCache.high >= 127;
    if (i >= IntegerCache.low && i <= IntegerCache.high)
        return IntegerCache.cache[i + (-IntegerCache.low)];
    return new Integer(i);
}

它使用了 IntegerCache,这是一个私有静态内部类,代码如下所示:

private static class IntegerCache {
    
    
    static final int low = -128;
    static final int high;
    static final Integer cache[];

    static {
    
    
        // high value may be configured by property
        int h = 127;
        String integerCacheHighPropValue =
            sun.misc.VM.getSavedProperty("java.lang.Integer.IntegerCache.high");
        if (integerCacheHighPropValue != null) {
    
    
            int i = parseInt(integerCacheHighPropValue);
            i = Math.max(i, 127);
            // Maximum array size is Integer.MAX_VALUE
            h = Math.min(i, Integer.MAX_VALUE - (-low) -1);
        }
        high = h;

        cache = new Integer[(high - low) + 1];
        int j = low;
        for(int k = 0; k < cache.length; k++)
            cache[k] = new Integer(j++);
    }

    private IntegerCache() {
    
    }
}

IntegerCache 表示 Integer 缓存,其中的 cache 变量是一个静态 Integer 数组,在静态初始化代码块中被初始化,默认情况下,保存了从 -128 到 127,共 256 个整数对应的 Integer 对象。

在 valueOf 代码中,如果数值位于被缓存的范围,即默认 -128 到 127,则直接从 IntegerCache 中获取已预先创建的 Integer 对象,只有不在缓存范围时,才通过 new 创建对象

通过共享常用对象,可以节省内存空间,由于 Integer 是不可变的,所以缓存的对象可以安全的被共享。Boolean/Byte/Short/Long/Character 都有类似的实现。这种共享常用对象的思路,是一种常见的设计思路,它有一个名字,叫享元模式,英文叫 Flyweight,即共享的轻量级元素。

7.1.4 剖析 Character

本节探讨 Character 类,Character 类除了封装了一个 char 外,还有什么可介绍的呢?它有很多静态方法,封装了 Unicode 字符级别的各种操作,是 Java 文本处理的基础,注意不是 char 级别,Unicode 字符并不等同于 char,本节详细介绍这些方法。

在此之前,先来回顾一下 Unicode 知识。

7.1.4.1 Unicode 基础

Unicode 给世界上每个字符分配了一个编号,编号范围从 0x000000 到 0x10FFFF。编号范围在 0x0000 到 0xFFFF 之间的字符,为常用字符集,称 BMP(Basic Multilingual Plane)字符。编号范围在 0x10000 到 0x10FFFF 之间的字符叫做增补字符(supplementary character)。

Unicode 主要规定了编号,但没有规定如何把编号映射为二进制。UTF-16 是一种编码方式,或者叫映射方式,它将编号映射为两个或四个字节,对 BMP 字符,它直接用两个字节表示,对于增补字符,使用四个字节,前两个字节叫高代理项(high surrogate),范围从 0xD800 到 0xDBFF,后两个字节叫低代理项(low surrogate),范围从 0xDC00 到 0xDFFF,UTF-16 定义了一个公式,可以将编号与四字节表示进行相互转换。

Java 内部采用 UTF-16 编码,char 表示一个字符,但只能表示 BMP 中的字符,对于增补字符,需要使用两个 char 表示,一个表示高代理项,一个表示低代理项。

使用 int 可以表示任意一个 Unicode 字符,低 21 位表示 Unicode 编号,高 11 位设为 0。整数编号在 Unicode 中一般称为代码点(Code Point),表示一个 Unicode 字符,与之相对,还有一个词代码单元(Code Unit)表示一个 char。

Character 类中有很多相关静态方法,让我们来看一下。

7.1.4.2 检查 code point 和 char

// 判断一个int是不是一个有效的代码单元,小于等于0x10FFFF的为有效,大于的为无效。
public static boolean isValidCodePoint(int codePoint) 
// 判断一个int是不是BMP字符,小于等于0xFFFF的为BMP字符,大于的不是。
public static boolean isBmpCodePoint(int codePoint) 
// 判断一个int是不是增补字符,0x010000和0X10FFFF之间的为增补字符。
public static boolean isSupplementaryCodePoint(int codePoint)
// 判断char是否是高代理项,0xD800到0xDBFF为高代理项。
public static boolean isHighSurrogate(char ch) 
// 判断char是否为低代理项,0xDC00到0xDFFF为低代理项。
public static boolean isLowSurrogate(char ch) 
// 判断char是否为代理项,char为低代理项或高代理项,则返回true。
public static boolean isSurrogate(char ch) 
// 判断两个字符high和low是否分别为高代理项和低代理项。
public static boolean isSurrogatePair(char high, char low) 
// 判断一个代码单元由几个char组成,增补字符返回2,BMP字符返回1。
public static int charCount(int codePoint) 

7.1.4.3 code point 与 char 的转换

除了简单的检查外,Character 类中还有很多方法,进行 code point 与 char 的相互转换。

// 根据高代理项high和低代理项low生成代码点,这个转换有个公式,这个方法封装了这个公式。
public static int toCodePoint(char high, char low)
// 根据代码点生成char数组,即UTF-16表示,如果code point为BMP字符,则返回的char数组长度为1
// 如果为增补字符,长度为2,char[0]为高代理项,char[1]为低代理项
public static char[] toChars(int codePoint) 
// 将代码点转换为char数组,与上面方法类似,只是结果存入指定数组dst的指定位置index
public static int toChars(int codePoint, char[] dst, int dstIndex) 
// 对增补字符code point,生成低代理项
public static char lowSurrogate(int codePoint)
// 对增补字符code point,生成高代理项
public static char highSurrogate(int codePoint) 

7.1.4.4 按 code point 处理 char 数组或序列

Character 包含若干方法,以方便按照 code point 来处理 char 数组或序列。

返回 char 数组 a 中从 offset 开始 count 个 char 包含的 code point 个数:

public static int codePointCount(char[] a, int offset, int count) 

比如说,如下代码输出为 2,char 个数为 3,但 code point 为 2。

char[] chs = new char[3];
chs[0] = '马';
Character.toChars(0x1FFFF, chs, 1);
System.out.println(Character.codePointCount(chs, 0, 3));

除了接受 char 数组,还有一个重载的方法接受字符序列 CharSequence:

public static int codePointCount(CharSequence seq, int beginIndex, int endIndex)

CharSequence 是一个接口,它的定义如下所示:

public interface CharSequence {
    
    
    int length();
    char charAt(int index);
    CharSequence subSequence(int start, int end);
    public String toString();
}

它与一个 char 数组是类似的,有 length 方法,有 charAt 方法根据索引获取字符,String 类就实现了该接口。

返回 char 数组或序列中指定索引位置的 code point:

public static int codePointAt(char[] a, int index)
public static int codePointAt(char[] a, int index, int limit)
public static int codePointAt(CharSequence seq, int index) 

如果指定索引位置为高代理项,下一个位置为低代理项,则返回两项组成的 code point,检查下一个位置时,下一个位置要小于 limit,没传 limit 时,默认为 a.length。

返回 char 数组或序列中指定索引位置之前的 code point:

public static int codePointBefore(char[] a, int index)
public static int codePointBefore(char[] a, int index, int start)
public static int codePointBefore(CharSequence seq, int index)

与 codePointAt 不同,codePoint 是往后找,codePointBefore 是往前找,如果指定位置为低代理项,且前一个位置为高代理项,则返回两项组成的 code point,检查前一个位置时,前一个位置要大于等于 start,没传 start 时,默认为 0。

根据 code point 偏移数计算 char 索引:

public static int offsetByCodePoints(char[] a, int start, int count,
                                         int index, int codePointOffset)
public static int offsetByCodePoints(CharSequence seq, int index,
                                         int codePointOffset)

如果字符数组或序列中没有增补字符,返回值为 index+codePointOffset,如果有增补字符,则会将 codePointOffset 看做 code point 偏移,转换为字符偏移,start 和 count 取字符数组的子数组。

比如,我们看如下代码:

char[] chs = new char[3];
Character.toChars(0x1FFFF, chs, 1);
System.out.println(Character.offsetByCodePoints(chs, 0, 3, 1, 1));

输出结果为 3,index 和 codePointOffset 都为 1,但第二个字符为增补字符,一个 code point 偏移是两个 char 偏移,所以结果为 3。

7.1.4.5 字符属性

我们之前说,Unicode 主要是给每个字符分配了一个编号,其实,除了分配编号之外,还分配了一些属性,Character 类封装了对 Unicode 字符属性的检查和操作,我们来看一些主要的属性。

获取字符类型(general category):

public static int getType(int codePoint)
public static int getType(char ch)

Unicode 给每个字符分配了一个类型,这个类型是非常重要的,很多其他检查和操作都是基于这个类型的。

getType 方法的参数可以是 int 类型的 code point,也可以是 char 类型,char 只能处理 BMP 字符,而 int 可以处理所有字符,Character 类中很多方法都是既可以接受 int,也可以接受 char,后续只列出 int 类型的方法。

返回值是 int,表示类型,Character 类中定义了很多静态常量表示这些类型,表 7-3 列出了一些字符,type 值,以及 Character 类中常量的名称:

在这里插入图片描述

检查字符是否在 Unicode 中被定义

public static boolean isDefined(int codePoint) 

每个被定义的字符,其 getType() 返回值都不为 0,如果返回值为 0,表示无定义。注意与 isValidCodePoint 的区别,后者只要数字不大于 0x10FFFF 都返回 true。

检查字符是否为数字

public static boolean isDigit(int codePoint)

getType() 返回值为 DECIMAL_DIGIT_NUMBER 的字符为数字,需要注意的是,不光字符 ‘0’,‘1’,…‘9’ 是数字,中文全角字符的 0 到 9,即 ‘0’,‘1’,‘9’ 也是数字。比如说:

char ch = '9'; //中文全角数字
System.out.println((int)ch+","+Character.isDigit(ch));

输出为:

65305,true

全角字符的 9,Unicode 编号为 65305,它也是数字。

检查是否为字母(Letter):

public static boolean isLetter(int codePoint)

如果 getType() 的返回值为下列之一,则为 Letter:

UPPERCASE_LETTER
LOWERCASE_LETTER
TITLECASE_LETTER
MODIFIER_LETTER
OTHER_LETTER

除了 TITLECASE_LETTER 和 MODIFIER_LETTER,其他我们上面已经看到过了,而这两个平时碰到的也比较少,就不介绍了。

检查是否为字母或数字

public static boolean isLetterOrDigit(int codePoint)

只要其中之一返回 true 就返回 true。

检查是否为字母(Alphabetic)

public static boolean isAlphabetic(int codePoint)

这也是检查是否为字母,与 isLetter 的区别是,isLetter 返回 true 时,isAlphabetic 也必然返回 true,此外,getType() 值为 LETTER_NUMBER 时,isAlphabetic 也返回 true,而 isLetter 返回 false。Letter_NUMBER 中常见的字符有罗马数字字符,如:‘Ⅰ’,‘Ⅱ’,‘Ⅲ’,‘Ⅳ’。

检查是否为空格字符

public static boolean isSpaceChar(int codePoint)

getType() 值为 SPACE_SEPARATOR,LINE_SEPARATOR 和 PARAGRAPH_SEPARATOR 时,返回 true。这个方法其实并不常用,因为它只能严格匹配空格字符本身,不能匹配实际产生空格效果的字符,如 tab 控制键 ‘\t’。

更常用的检查空格的方法

public static boolean isWhitespace(int codePoint) 

‘\t’,’\n’,全角空格’ ‘,和半角空格’ '的返回值都为 true。

检查是否为小写字符

public static boolean isLowerCase(int codePoint) 

常见的主要就是小写英文字母 a 到 z。

检查是否为大写字符

public static boolean isUpperCase(int codePoint)

常见的主要就是大写英文字母 A 到 Z。

检查是否为表意象形文字

public static boolean isIdeographic(int codePoint) 

大部分中文都返回为 true。

检查是否为 ISO 8859-1 编码中的控制字符

public static boolean isISOControl(int codePoint) 

我们在第 2 章介绍过,0 到 31,127 到 159 表示控制字符。

检查是否可作为 Java 标示符的第一个字符

public static boolean isJavaIdentifierStart(int codePoint) 

Java 标示符是 Java 中的变量名、函数名、类名等,字母(Alphabetic),美元符号($),下划线(_)可作为 Java 标示符的第一个字符,但数字字符不可以。

检查是否可作为 Java 标示符的中间字符

public static boolean isJavaIdentifierPart(int codePoint) 

相比 isJavaIdentifierStart,主要多了数字字符,中间可以有数字。

检查是否为镜像(mirrowed)字符

public static boolean isMirrored(int codePoint)

常见镜像字符有 ( ) { } < > [ ],都有对应的镜像。

7.1.4.6 字符转换

Unicode 除了规定字符属性外,对有大小写对应的字符,还规定了其对应的大小写,对有数值含义的字符,也规定了其数值。

我们先来看大小写,Character 有两个静态方法,对字符进行大小写转换

public static int toLowerCase(int codePoint)
public static int toUpperCase(int codePoint)

这两个方法主要针对英文字符 a-z 和 A-Z, 例如:toLowerCase(‘A’) 返回 ‘a’,toUpperCase(‘z’) 返回 ‘Z’。

返回一个字符表示的数值

public static int getNumericValue(int codePoint)  

字符 ‘0’ 到 ‘9’ 返回数值 0 到 9,对于字符 a 到 z,无论是小写字符还是大写字符,无论是普通英文还是中文全角,数值结果都是 10 到 35,例如,如下代码的输出结果是一样的,都是 10。

System.out.println(Character.getNumericValue('A')); //全角大写A
System.out.println(Character.getNumericValue('A'));
System.out.println(Character.getNumericValue('a')); //全角小写a
System.out.println(Character.getNumericValue('a'));

返回按给定进制表示的数值

public static int digit(int codePoint, int radix) 

radix 表示进制,常见的有 2/8/10/16 进制,计算方式与 getNumericValue 类似,只是会检查有效性,数值需要小于 radix,如果无效,返回 -1,例如:

digit(‘F’,16) 返回 15,是有效的,但 digit(‘G’,16) 就无效,返回 -1。

返回给定数值的字符形式

public static char forDigit(int digit, int radix) 

与 digit(int codePoint, int radix) 相比,进行相反转换,如果数字无效,返回 ‘\0’。例如,Character.forDigit(15, 16) 返回 ‘F’。

与 Integer 类似,Character 也有按字节翻转

public static char reverseBytes(char ch)

例如,翻转字符 0x1234:

System.out.println(Integer.toHexString(
                Character.reverseBytes((char)0x1234)));

输出为 3412。

猜你喜欢

转载自blog.csdn.net/bm1998/article/details/107879509