java字符编码转换本质是字节与unicode字符串的双向编解码过程,string内存中恒为utf-16,getbytes()结果取决于指定编码;“gbk转utf-8”需先用gbk解码得unicode字符串,再用utf-8编码生成新字节流,中间string无编码属性。

Java 字符编码转换不是类型强转,而是字节与 Unicode 字符串之间的双向解码/编码过程;内存中 String 始终以 Unicode(UTF-16)形式存在,但 getBytes() 返回的字节数取决于目标编码,而非 char 个数或固定倍数。
字符编码转换的本质是两次独立操作
所谓“GBK 转 UTF-8”,实际分两步:先用 GBK 解码原始字节流,得到内存中的 Unicode 字符串;再用 UTF-8 编码该字符串,生成新字节流。中间的 String 是 Unicode 中立的桥梁,不带任何编码属性。
- 错误做法:
new String(gbkBytes, "UTF-8")—— 若 gbkBytes 实际是 GBK 编码字节,却用 UTF-8 解码,必然乱码 - 正确流程:
new String(rawBytes, "GBK")→ 得到正确 Unicode 字符串 →.getBytes("UTF-8")→ 得到 UTF-8 字节 - 推荐使用
StandardCharsets枚举(如StandardCharsets.UTF_8),避免字符串拼写错误和异常抛出
char 和 String 的内存占用不等于编码字节数
Java 的 char 固定占 2 字节(UTF-16 码元),String 在 JDK 9+ 可能用 byte[] 存 Latin-1 字符(1 字节/字符),但这些都只是内部优化。对外暴露的 getBytes() 结果完全由指定编码决定:
- '中' 在 UTF-8 中占 3 字节(
E4 B8 AD),在 GBK 中占 2 字节(D6 D0),在 ISO-8859-1 中无法表示(抛UnsupportedEncodingException) -
"你好".getBytes("UTF-8").length返回 6,.getBytes("GBK").length返回 4,与 char 数量(2)无直接倍数关系 - 避免依赖
getBytes()无参重载——它用系统默认编码,跨环境极易出错
文件与网络 IO 中必须显式指定编码
用 InputStream/OutputStream 处理文本时,若不包装为 Reader/Writer,就只是字节搬运工,不会自动编解码;而 InputStreamReader / OutputStreamWriter 才真正承担字符集转换职责:
- 读文件:
new InputStreamReader(new FileInputStream("a.txt"), StandardCharsets.GBK) - 写文件:
new OutputStreamWriter(new FileOutputStream("b.txt"), StandardCharsets.UTF_8) - Socket 场景下,务必在发送前用目标编码生成字节,接收后用相同编码解析字节,否则两端对不上
- HTTP 响应中,需同时设置
response.setCharacterEncoding("UTF-8")和response.setContentType("text/html; charset=UTF-8")
排查乱码要从数据源头开始定位
乱码不是单一环节的问题,而是编码链上任一环节错配的结果。常见断点包括:
- 源文件保存编码(如记事本存为 ANSI 实际是 GBK,VS Code 默认 UTF-8)
- 代码中读取时指定的 Charset 是否与源文件一致
- 数据库连接 URL 是否含
useUnicode=true&characterEncoding=UTF-8 - Tomcat server.xml 的
URIEncoding="UTF-8"是否配置 - Linux 终端、IDE 控制台、日志框架输出编码是否统一
不复杂但容易忽略
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











