字符型 char
本章定位:char是Java的16位Unicode字符类型,基于UTF-16编码,取值范围0~65535(
\u0000~\uFFFF)。在飞翔科技的中文网站系统中,从"飞""翔""中""文""网"到"F""E""I""X""I""A""N""G",每一个字符都是char。小崔曾因不了解增补字符(Supplementary Characters)导致emoji被截断,白歌给他补了一课Unicode。
定义与语法
| 属性 | 说明 |
|---|---|
| 关键字 | char(字符型) |
| 位数 | 16 bits(2字节) |
| 编码 | UTF-16(Unicode字符集的16位编码单元) |
| 取值范围 | \u0000 ~ \uFFFF(即 0 ~ 65535,无符号) |
| 默认值 | \u0000(空字符,不是空格) |
| 包装类 | java.lang.Character |
| 字面量 | 单引号括起:'A'、'飞'、'\u0041'、'\n'、65(int字面量) |
基本声明
char productGrade = 'A'; // 商品等级A
char fuelType = 'X'; // 油品类型X=汽油
char salaryGrade = 'B'; // 工资等级
char scenicLevel = 'A'; // 景区A级
char houseType = '三'; // 三室一厅
char seatRow = 'F'; // F排座位
深度原理:char与Unicode
Java char 的 Unicode 本质
关键认知:
- Java中
char是16位无符号整数,本质上等同于UTF-16编码的一个代码单元(code unit) - 基本多文种平面(BMP)中的字符(U+0000~U+FFFF)用一个char表示
- 增补字符(U+10000~U+10FFFF,如大部分emoji)需要两个char(代理对)
- 这意味着:一个char不一定等于一个完整字符!
char的多种表示法
| 表示法 | 格式 | 示例 | 说明 |
|---|---|---|---|
| 直接字符 | 'A' | char c = 'A'; | ASCII字符 |
| 中文字符 | '飞' | char c = '飞'; | BMP中的CJK字符 |
| Unicode转义 | '\uXXXX' | char c = '\u98DE'; | 4位十六进制 |
| 八进制转义 | '\0'~'\377' | char c = '\101'; | 八进制(A的ASCII码=101₈) |
| 特殊转义 | '\n' '\t' '\\' | char tab = '\t'; | 控制字符 |
| 整数赋值 | 65 | char c = 65; | 字面量在char范围内 |
完整代码示例
示例一:飞翔中文网——中文与Unicode转义
/**
* 飞翔科技 - char 类型演示
* 场景:飞翔中文网的中文字符处理
*/
public class CharChineseDemo {
public static void main(String[] args) {
// 中文字符数组
char[] chineseName = {'飞', '翔', '中', '文', '网'};
char[] englishName = {'F', 'E', 'I', 'X', 'I', 'A', 'N', 'G', '.', 'N', 'E', 'T'};
char[] year = {'2', '0', '1', '8'}; // 成立年份
// Unicode 转义形式
char feiChar = '\u98DE'; // 飞的Unicode编码
char xiangChar = '\u7FD4'; // 翔的Unicode编码
System.out.print("中文名称:");
for (char c : chineseName) {
System.out.print(c);
}
System.out.println();
System.out.print("英文名称:");
for (char c : englishName) {
System.out.print(c);
}
System.out.println();
System.out.print("成立年份:");
for (char c : year) {
System.out.print(c);
}
System.out.println();
// Unicode验证
System.out.println("\n【Unicode编码验证】");
System.out.println("\\u98DE = " + feiChar);
System.out.println("\\u7FD4 = " + xiangChar);
System.out.println("'飞' == \\u98DE ? " + ('飞' == '\u98DE'));
// char的本质是整数
System.out.println("\n【char的整数本质】");
System.out.println("'A'的数值:" + (int) 'A'); // 65
System.out.println("'飞'的数值:" + (int) '飞'); // 39134
System.out.println("'翔'的十六进制:" + Integer.toHexString('翔')); // 7fd4
}
}
运行输出:
中文名称:飞翔中文网
英文名称:FEIXIANG.NET
成立年份:2018
【Unicode编码验证】
\u98DE = 飞
\u7FD4 = 翔
'飞' == \u98DE ? true
【char的整数本质】
'A'的数值:65
'飞'的数值:39134
'翔'的十六进制:7fd4
示例二:飞翔教育——成绩等级判定
/**
* 飞翔科技 - char 等级判定演示
* 场景:飞翔大学的成绩等级系统
*/
public class CharGradeDemo {
public static void main(String[] args) {
// 成绩等级
char scoreRank = 'A'; // 成绩A等
char gradeLevel = '甲'; // 班级等级
char bloodType = 'O'; // 血型O
System.out.println("【飞翔大学 - 成绩单】");
System.out.println("成绩等级:" + scoreRank + "等");
System.out.println("班级等级:" + gradeLevel + "班");
System.out.println("血型:" + bloodType + "型");
// char的比较(本质是整数比较)
System.out.println("\n【等级比较】");
char rank1 = 'A';
char rank2 = 'B';
char rank3 = 'C';
System.out.println("'A' < 'B' ? " + (rank1 < rank2)); // true(65 < 66)
System.out.println("'B' < 'C' ? " + (rank2 < rank3)); // true
// char参与算术运算
System.out.println("\n【char运算】");
char base = 'A';
char next = (char) (base + 1);
System.out.println("'A' + 1 = " + next); // B
// 判断字符类型
System.out.println("\n【字符类型判断】");
System.out.println("'A'是字母?" + Character.isLetter('A'));
System.out.println("'5'是数字?" + Character.isDigit('5'));
System.out.println("'飞'是字母?" + Character.isLetter('飞')); // true!(CJK字符算字母)
System.out.println("' '是空白?" + Character.isWhitespace(' '));
System.out.println("'\\n'是空白?" + Character.isWhitespace('\n'));
}
}
运行输出:
【飞翔大学 - 成绩单】
成绩等级:A等
班级等级:甲班
血型:O型
【等级比较】
'A' < 'B' ? true
'B' < 'C' ? true
【char运算】
'A' + 1 = B
【字符类型判断】
'A'是字母?true
'5'是数字?true
'飞'是字母?true
' '是空白?true
'\n'是空白?true
易错场景
反例一:char的空字符 vs 空格
// ❌ 易混淆:默认值 \u0000 不是空格 ' '
char ch; // 成员变量,默认值为 \u0000
// System.out.println(ch == ' '); // false!\u0000 不是空格
// System.out.println(ch == '\u0000'); // true
// \u0000 是空字符(null character),打印时不可见
// ' ' 是空格,ASCII码32
反例二:char不能表示所有Unicode——emoji陷阱
// ❌ 危险:emoji长度超过1个char
// char emoji = '😀'; // 编译错误!emoji(U+1F600)超出BMP范围
// ✅ 正确:emoji需要2个char(代理对)
String emoji = "😀";
System.out.println("emoji长度:" + emoji.length()); // 2(两个char!)
System.out.println("emoji码点数:" + emoji.codePointCount(0, emoji.length())); // 1
// Unicode 代理对范围
// 高代理:\uD800 ~ \uDBFF
// 低代理:\uDC00 ~ \uDFFF
反例三:char运算自动提升为int
// ❌ 易错:char + char 结果是 int
char a = 'A';
char b = 'B';
int sum = a + b; // 131(65 + 66)
// char result = a + b; // 编译错误!需要强转
char result = (char) (a + b); // 强制转换(不推荐)
// char 会自动提升:char → int → long → float → double
反例四:'A' 和 "A" 的混淆
// ❌ 类型错误:单引号是char,双引号是String
char ch = "A"; // 编译错误!String不能赋给char
char ch2 = 'A'; // ✅ 正确
// char 是基本类型,String 是引用类型
// char 用 == 比较值,String 用 equals() 比较内容
常见转义字符表
| 转义序列 | 含义 | Unicode | 数值 |
|---|---|---|---|
'\n' | 换行 | \u000A | 10 |
'\r' | 回车 | \u000D | 13 |
'\t' | 制表符Tab | \u0009 | 9 |
'\\' | 反斜杠 | \u005C | 92 |
'\'' | 单引号 | \u0027 | 39 |
'\"' | 双引号 | \u0022 | 34 |
'\0' | 空字符 | \u0000 | 0 |
面试考点
Q1:Java的char为什么是2字节?
Java设计于1990年代中期,当时Unicode 1.0只有16位编码空间(U+0000~U+FFFF),因此Java将char设计为16位无符号整数,使用UTF-16编码。后来Unicode扩展到U+10FFFF(需要21位),超出BMP的字符需要用两个char(代理对)表示。这一设计决策成为Java的"历史包袱"——char不能完整表示所有Unicode字符。
Q2:char c = '中'; 在内存中存的是什么?
存储的是
'中'的Unicode码点值20013(即十六进制\u4E2D),在16位内存中为01001110 00101101。char本质上是一个16位无符号整数(0~65535),可以参与算术运算和比较。
Q3:String.length()和String.codePointCount()有什么区别?
length()返回char单元的数量(UTF-16 code units),对于增补字符(如emoji),一个码点占2个char单元,所以length()可能大于实际字符数。codePointCount()返回Unicode码点的数量(即"真正"的字符数)。例如:"😀".length()=2,"😀".codePointCount(0,2)=1。
Q4:char类型能存中文吗?能存emoji吗?
能存BMP内的中文(U+4E00~U+9FFF,即常用汉字),因为它们的Unicode码点≤U+FFFF,一个char足够。不能直接存emoji(大部分emoji在U+1F300~U+1F9FF),因为它们的码点>U+FFFF,需要两个char(代理对),单个char变量无法表示。要完整处理Unicode,应使用
String和codePointAt()等API。