一文讲通Unicode规范、UTF-8与UTF-16编码及在Java中的验证

结论:

  1. java中的char类型表示的是UTF-16编码中的一个码元,所以一个char类型只能表示第0级别(BMP)中的符号,其他级别中的符号需要用两个char变量才能表示。char用U+xxxx的方式赋值时,规定xxxx的范围是[0000->FFFF],所以补充平面中的符号是不能直接通过U形式的赋值到一个char变量直接得到的(因为需要两个变量)。
  2. java中的char及它的包装类、String、StringBuffer类使用UTF-16编码,在字节码中使用改进的utf-8编码,而在字节码加载到内存中执行的时候,又把缩略码转化为UTF-16的表示形式。字节码改变形式可能是因为出于对节省空间缩小文件体积的考量。
  3. 关于java中String内部存储方式发生变化的说明:
    • 在jdk9之后,String的内部存储由char[]改为了byte[]和一个标志位,这个改变有利于节省空间。

一、Unicode

Unicode是为了统一不同字符间的交互与编码问题而制定的一种规范,旨在对世界上所有可数字化的书写系统中的所有文字进行文本编码。

在Unicode规范中,每一个可书写字符都有一个唯一码点(code point),也就是说每一个字符都由一个码点唯一确定。

Unicode可使用多种编码方式进行处理和存储二进制数据,Unicode标准中主要 定义了三种编码方式:UTF-8、UTF-16与UTF-32,其中UTF-8的使用最为广泛。

二、UTF-8编码规则

练习打字的时候有一个常识,即汉字的大部分文本都是由常见的前500字组成的,如果要练打字速度的话,只需要把常见的前500个字练好就可以了。为了保险起见,可以练习常见的前1500个字。也就是"二八定律"。

同样,在编码中,数值较低的码点代表的符号出现的频率通常较高,因此低码点的数字需尽量的使用较少的字节进行编码。ASCII码的码点范围是从0-127共128个数,在UTF-8编码中,ASCII码的编码需与其自身相同。

UTF-8可以使用一个字节、二个字节、三个字节与四个字节进行编码。这四种编码方式的表示范围与编码方式如下所示。

从这个表中可以看出:

  1. 使用一个字节编码时,最高位取0,用其余七个数来表示具体的值,正好是128个,与ASCII码范围完全一致。
  2. 首字节的前几位已固定,如果1的个数是n,则使用n个字节来编码。非首字节以10开始。

具体的编码规则为:

以'中'的编码为例:

通过java程序可以得出'中'的码点为0x4E2D,UTF-8编码为:U+E4B8AD。 与按照编码规则手动计算的结果一致:

java中对于char类型和String类型使用的是utf-16编码,在字节码文件中常量池中使用的是utf-8的缩略编码,3个字节的缩略编码与正常编码是一样的,所以'中'字在字节码文件中一定也是正常utf-8的三字节表示方式;python中使用的是utf-8编码;C语言通过设置编码格式也可以使得char类型使用utf-8编码。

java中的验证

字节码文件中可以看到第一行为sipush 20013,即将20013入栈。下一个执行的指令为istore_1,即从操作数栈中弹出栈顶的int(此处就是20013),然后将弹出的数存储到局部变量表索引为1的位置。

查看局部变量表可知,索引为0的位置存储的是main方法的形参args,1位置存储的是局部变量c,即将20013赋值给c变量。此处的20013转换为16进制就是0x4e2d.

三、UTF-16编码规则

Unicode中的Plane(平面)

在Unicode标准中,平面是由65536 ( 2 16 ) (2^{16}) (216)个连续码点组成的组。将所有码点划分为17个平面,对于16进制的00至10。其中第0个平面称为BMP(Basic Multilingual Plane),包含了最常用的字符。其余的16个平面称为补充平面。这17个平面中的最后一个码点为:U+10FFFF.

UTF-16编码的长度也是可变的,可以使用一个或者两个16-bit的代码单元(也就是2字节或者4字节)。

在这个概念下,第0个级别的范围为U+0000->U+0FFFF,将这个级别分为三个范围:

  1. U+0000->U+D7FF
  2. U+D800->U+DFFF
  3. U+E000->U+FFFF

编码规则为:

  1. 在BMP中,用两个字节表示

  2. 在补充平面中,用四个字节表示

    • 补充平面的表示范围为:U+10000->U+10FFFF,这个范围内的码点编码时需要先减去U+10000,减完之后,范围就会变为[U+0000->U+FFFFF],也就是2^{20}个数,需要用20位来表示。
    • 把这20位拆成高10位和低10位,高10位需要加上0xD800。这16位的表示范围就会变为:[U+D800->0xDBFF]对应于0级别的第二个范围的前半部分。
    • 低10位加上0xDC00,表数范围就会变为:[U+DC00->DFFFF],对应于0级别的第二个范围的后半部分。与高10位组合出来的范围恰好完整构成0级别的整个第2段范围。

java中的验证

相关推荐
AntsData28 分钟前
用 12K+ 条亚马逊评论信号,定位机械键盘下一版该改什么
java·大数据·计算机外设
code2cat36 分钟前
【随笔】连续批处理:长短请求怎样在迭代之间补位
java·人工智能·后端·大语言模型·vllm
最强小杰39 分钟前
DeepSeek V4 Flash + Codex CLI 接入教程:原生 Responses API 适配,从配置到跑通收藏这篇就够了(2026)
java·前端·python·ai
Flynn Liang1 小时前
Spring AI 2.0 深度解析:ChatClient、Advisor 链与 MCP——Java 工程师的 AI 落地最优解
java·人工智能·spring·大模型·架构设计·spring ai
灿飞1 小时前
Kotlin与Java虚拟机六:基础与类型系统:一切皆对象,但对象有代价
java·kotlin
计算机毕设定制辅导-无忧学长1 小时前
《基于微信小程序健康习惯激励系统设计与实现》
java·spring boot·mysql·微信小程序·uniapp·健康习惯激励系统
雪芽蓝域zzs2 小时前
第 1 章:IDEA 创建 SpringBoot3.5.16 Maven 项目
java·maven·intellij-idea
golang学习记2 小时前
IDEA 开始换芯,发布全新AI版终端
java·人工智能·intellij-idea
vipxieliang2 小时前
ValidX 正则表达式预编译优化:Pattern 复用策略
java·spring boot·后端
Wang's Blog3 小时前
Java 项目实战:交友项目 - FastDFS 工作原理
java·大数据·交友