Java中字符型数据在内存中始终以Unicode(UTF-16)形式存储,字符串操作均基于此Unicode表示;编码转换本质是字节与字符串间两次显式编解码,乱码只发生在边界处。

Java 中字符型数据在内存中始终以 Unicode(UTF-16)形式存储,这是所有转换的起点和终点。所谓“编码转换”,本质是字节 ↔ 字符串之间的两次显式操作:先按源编码解码为 Unicode 字符串,再按目标编码重新编码为字节。没有一步到位的“强转”,也没有“String 转 GBK”这种语法。
字符串在内存中怎么存?
Java 的 char 是 16 位无符号整数,代表一个 UTF-16 码元;String 内部由 char 数组或压缩后的 byte 数组(JDK 9+)构成,逻辑上始终表示 Unicode 文本。这意味着:
- 无论你从文件读入的是 UTF-8 还是 GBK 字节,只要正确解码,得到的 String 对象内容一致
- 打印、拼接、正则匹配等所有字符串操作,都基于这个 Unicode 表示,与原始编码无关
- 乱码一定发生在“字节 → String”或“String → 字节”这两次边界上,而非 String 内部
最常用:用 getBytes() 和 String 构造器完成转换
适用于已知原始编码和目标编码的明确场景,比如将 GBK 编码的字节数组转成 UTF-8 字符串:
- 先用原始编码构造 String:new String(rawBytes, "GBK") —— 把字节按 GBK 解码为 Unicode 字符串
- 再用目标编码获取字节:str.getBytes("UTF-8") —— 把 Unicode 字符串按 UTF-8 编码为新字节数组
- ⚠️ 注意:不能反着写
new String(rawBytes, "UTF-8")去解 GBK 字节,否则必然乱码 - 避免使用无参构造器或无参 getBytes(),它们依赖系统默认编码,不可移植
流式处理:用 InputStreamReader / OutputStreamWriter
适合文件读写、网络通信等需要持续处理文本的场景,自动完成编解码,无需手动管理字节数组:
立即学习“Java免费学习笔记(深入)”;
- 读文件(GBK → Unicode):new InputStreamReader(new FileInputStream("a.txt"), "GBK")
- 写文件(Unicode → UTF-8):new OutputStreamWriter(new FileOutputStream("b.txt"), "UTF-8")
- 配合 BufferedReader / BufferedWriter 可逐行读取,更高效安全
- 比纯字节数组方式更健壮,尤其对大文件或带 BOM 的文件支持更好
进阶控制:用 Charset 和 CharsetEncoder/Decoder
当需要处理编码异常、检测 BOM、设置替换字符或实现自定义行为时,推荐使用标准 NIO API:
- Charset.forName("UTF-8") 获取编码器实例
- charset.newDecoder().onMalformedInput(CodingErrorAction.REPLACE) 定义非法字节如何处理
- charset.encode(charBuffer) 或 charset.decode(byteBuffer) 执行转换
- 比老式 API 更灵活,且线程安全,适合工具类封装


















