java字符串在内存中始终以utf-16编码的unicode序列存储,对外传输时才按指定编码(如utf-8或gbk)转换为字节流;getbytes()和new string(byte[], charset)本质是unicode与目标编码间的双向转换,必须经unicode中转,不可直接跨编码强转。

Java 字符编码不是“字符串用了什么编码”,而是“字符串在内存里是 Unicode,往外走时才选编码”。理解这点,就抓住了乱码、字节长度、转换异常等问题的根。
String 在内存中永远是 UTF-16(Unicode)
Java 的 String 对象不存“UTF-8”或“GBK”,它内部用的是 UTF-16 编码的 Unicode 序列:
- 基本平面字符(如英文字母、常用汉字“中”U+4E2D)占 2 字节,对应一个
char - 超出基本平面的字符(如 emoji ?、生僻汉字)需两个
char组成代理对(Surrogate Pair),共占 4 字节 -
String.length()返回的是char个数,不是真实字符数;要用codePointCount(0, s.length())才准确
getBytes() 的结果完全取决于你指定的编码
调用 string.getBytes("UTF-8") 或 getBytes("GBK"),本质是把内存中的 Unicode 字符,按目标编码规则“翻译”成字节流:
- 英文字符 'A':UTF-8 → 1 字节,GBK → 1 字节(兼容 ASCII)
- 汉字 “中”:UTF-8 → 3 字节(
E4 B8 AD),GBK → 2 字节(D6 D0) - emoji “?”:UTF-8 → 4 字节,GBK → 抛
UnsupportedEncodingException(GBK 不支持) -
不传编码参数(如
getBytes())会用 JVM 默认 charset,Windows 下常为 GBK,Linux/macOS 多为 UTF-8 —— 这是跨平台乱码主因
编码转换必须经过 Unicode 中转
Java 的转换逻辑是严格单向的:字节 → 解码为 Unicode String → 再编码为目标字节。没有直接“UTF-8 到 GBK”的捷径:
- 错误写法:
new String(utf8Bytes, "GBK")—— 这会把 UTF-8 字节误当作 GBK 解码,必然乱码 - 正确流程:
new String(utf8Bytes, "UTF-8")得到正确 String,再调.getBytes("GBK")输出 GBK 字节 - 推荐用
Charset显式控制:StandardCharsets.UTF_8.decode(byteBuffer).toString()和StandardCharsets.GBK.encode(charBuffer)
实战避坑要点
日常开发中,这几个细节最容易出问题:
- 读文件时:用
Files.readString(path, StandardCharsets.UTF_8),别依赖默认编码 - 写文件时:明确指定
Files.write(path, bytes, StandardCharsets.UTF_8) - 网络请求(如 HTTP):响应头
Content-Type: text/html; charset=utf-8必须与实际解码一致 - IDE 和项目设置:确保编辑器编码、编译编码、Maven/Gradle 配置统一为 UTF-8
- JVM 启动参数加
-Dfile.encoding=UTF-8,避免 Windows 下默认 GBK 干扰
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











