温馨提示×

温馨提示×

您好,登录后才能下订单哦!

密码登录×
登录注册×
其他方式登录
点击 登录注册 即表示同意《亿速云用户服务条款》

Java Unicode统一码如何存储

发布时间:2026-08-27 22:10:32 来源:亿速云 阅读:103 作者:小樊 栏目:编程语言

Java 语言规范规定,Java 内部使用 Unicode 字符集。在不同的层面(源码、编译后的 Class 文件、运行时内存),Java 对 Unicode 的存储方式有一些特定的设计和演进。

以下是 Java 存储 Unicode 的核心机制:

1. 基本存储单元:char 与 UTF-16

在 Java 中,基本字符类型 char 固定占用 16 位(2 字节),它采用的是 UTF-16 编码的一个“代码单元(Code Unit)”。

  • 由于 16 位可以表示 $2^{16} = 65536$ 个码位,这刚好能够覆盖 Unicode 的基本多文种平面(BMP,U+0000 到 U+FFFF)。我们常用的英文字母、中文汉字等绝大部分字符都在这个范围内,因此一个 char 就可以存储一个常用字符

2. 补充平面字符(代理对机制)

Unicode 中还有一些字符位于“补充平面”(如部分生僻汉字、Emoji 表情等,码点大于 U+FFFF)。由于 16 位空间不够,Java 采用了 UTF-16 的**代理对(Surrogate Pair)**机制:

  • 使用两个 char(共 4 字节)来组合表示一个补充平面的 Unicode 码点(Code Point)。
  • 因此,一个完整的 Unicode 字符在 Java 中可能需要 1 个或 2 个 char。为了能完整表示任意 Unicode 字符,Java 中通常用 int(32 位)类型来代表一个完整的“码点”(例如在 Character.codePointAt() 等 API 中)。

3. String 在运行时内存中的存储

String 是 Java 中使用最频繁的类型,它在内存中的存储方式在 JDK 9 前后发生了重要变化:

  • JDK 8 及以前String 内部通过一个 char[] 数组存储,不管是什么字符,每个字符都固定占用 2 字节(纯 UTF-16)。这种方式对于纯英文或数字(Latin-1)场景来说非常浪费内存。
  • JDK 9 及以后(Compact Strings 压缩字符串):为了极大节省内存,String 内部改为使用 byte[] 数组,并带有一个编码标记(coder):
    • 如果字符串只包含 Latin-1 字符(U+0000 到 U+00FF,如英文字母、数字),则每个字符仅占用 1 字节
    • 只要包含任意一个超出 Latin-1 的字符(如中文),整个字符串就会退化为 UTF-16 格式存储,每个字符占用 2 字节
    • 注意:Java 在内存中并没有直接用 UTF-8 这种变长编码来存 String,而是采用“全 Latin-1(定长1字节)”或“全 UTF-16(定长2字节)”的方式,以便于字符的随机访问。

4. 源文件与 Class 文件(字节码)中的存储

  • .java 源文件:通常我们在磁盘上以 UTF-8 编码保存源文件,源码中可以直接写中文或表情符号(当然也可以使用 \uXXXX 的 Unicode 转义序列)。
  • Class 文件常量池:编译后,字符串字面量会被存入 Class 文件的常量池中。根据 JVM 规范,Class 文件中使用的是 Modified UTF-8(修改后的 UTF-8) 格式。它与标准 UTF-8 略有区别,例如:null 字符会被编码为两字节、补充平面字符会被拆分为代理项编码为 6 个字节,这样可以确保字符串数据中没有嵌入的零字节,方便 C 语言底层处理。

5. 开发中的常见“坑”与建议

由于 Java 采用 UTF-16 代码单元(char)作为基础,开发者在处理字符串时需要注意:

  • String.length() 的陷阱:该方法返回的是 char 的数量(代码单元数),而不是肉眼看到的“字符”数。例如一个 Emoji 表情(代理对)的 length() 会返回 2
  • 获取真实字符数:如果需要获取真正的 Unicode 字符数(码点数),应使用 string.codePointCount(0, string.length()) 或流式遍历 string.codePoints()
  • 截取字符串:使用 substring() 时要小心,如果截取位置恰好把一个“代理对(如 Emoji)”从中间切断,就会产生乱码(显示为 )。
  • IO 与序列化:当字符串需要写入磁盘文件或通过网络传输时,必须显式指定编码(如 .getBytes(StandardCharsets.UTF_8)),不要依赖平台默认编码,否则极易导致乱码。

总结:Java 在内存中主要用 UTF-16(JDK 9+ 优化为 Latin-1/UTF-16 混合)来存储字符,在 Class 字节码中用 Modified UTF-8 存储,而在外部文件或网络传输时则通常使用标准的 UTF-8 进行编解码。

向AI问一下细节

免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。

AI